简体   繁体   English

Spring 数据 jpa 本机查询与 null 参数 (PostgreSQL)

[英]Spring data jpa native query with null parameter (PostgreSQL)

The question is how to make my native query accept a null parameter?问题是如何让我的本机查询接受 null 参数?

Here is my query:这是我的查询:

    @Query(value = "SELECT " +
            "   summaries.event_type as \"eventType\", " +
            "   summaries.institution_id as \"institutionId\", " +
            "   identifiers.id as \"studentIdentifierId\", " +
            "   identifiers.name as \"studentIdentifierName\" " +
            "FROM summaries " +
            "LEFT JOIN roles ON summaries.student_id=roles.id " +
            "LEFT JOIN identifiers ON roles.identifier_id=identifiers.id " +
            "WHERE " +
            "   summaries.student_id IS NOT NULL AND " +
            "   summaries.event_type IN (:eventTypes) AND " +
            "   (:identifierId IS NULL OR roles.identifier_id=:identifierId) " +
            "GROUP BY " +
            "   summaries.institution_id, " +
            "   summaries.student_id, " +
            "   identifiers.id, " +
            "   identifiers.name, " +
            "   summaries.event_type",
            nativeQuery = true)
    List<CustomProjection> findByCustomCondition(
            @Param("identifierId") Long identifierId,
            @Param("eventTypes") List<String> eventTypes);

And now when I pass identifierId as null, I'm receiving the error:现在,当我将identifierId作为 null 传递时,我收到了错误消息:

InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet.
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint = bytea
  HINT: No operator matches the given name and argument types. You might need to add explicit type casts.

It seems like the line (:identifierId IS NULL OR roles.identifier_id=:identifierId) causes a problem here, but can't really find a good solution, as from my understanding it should work normally (when I use this query directly on pgAdmin it works as it should, so I'm quite sure it's an issue related with mapping).似乎这行(:identifierId IS NULL OR roles.identifier_id=:identifierId)在这里引起了问题,但实际上找不到一个好的解决方案,因为据我了解它应该可以正常工作(当我直接在 pgAdmin 上使用此查询时它可以正常工作,所以我很确定这是与映射有关的问题)。

I tried to cast the identifier ( cast(:identifierId as bigint) ), but it didn't helped out.我试图转换标识符( cast(:identifierId as bigint) ),但它没有帮助。

PS. PS。 it's kinda legacy project, so these are versions that I use:这是一个遗留项目,所以这些是我使用的版本:

compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.11.6.RELEASE'
compile group: 'org.postgresql', name: 'postgresql', version: '42.2.2'
compile group: 'org.hibernate', name: 'hibernate-core', version: '5.2.17.Final'

This is a common problem with Hibernate + PostgreSQL, and the solution is to implement that method yourself, instead of having Spring do it for you.这是 Hibernate + PostgreSQL 的常见问题,解决方法是自己实现该方法,而不是让 Spring 为您实现。 Within the implementation you have to do something like this在实现中,你必须做这样的事情

List<CustomProjection> findByCustomCondition(
        @Param("identifierId") Long identifierId,
        @Param("eventTypes") List<String> eventTypes) {
     // entityManager is acquired with the @PersistenceContext annotation as an injectable
     Query q = entityManager.createNativeQuery(..., CustomProjection.class);
     // the important part:
     q.setParameter("identifierId", 0L);
     q.setParameter("identifierId", identifierId);
     ...

First call to setParameter ensures Hibenate uses the correct type setter, second call overrides the first value without having executed the query, and skips type detection.第一次调用setParameter确保 Hibenate 使用正确的类型设置器,第二次调用在没有执行查询的情况下覆盖第一个值,并跳过类型检测。

通过在roles.identifier_id=:identifierId之间添加空格来修改查询应该像roles.identifier_id= :identifierId

您将不得不使用以下内容: (cast(:identifierId as bigint) IS NULL OR roles.identifier_id=:identifierId)

As it was mentioned in the @coladict's answer it looks like, unfortunately, you can not do that you want with clean Spring Data JPA solution.正如@coladict 的回答中提到的那样,不幸的是,您无法使用干净的 Spring Data JPA 解决方案来做到这一点。 So, you can rewrite you query using EntityManager and hibernate specific setParameter that allows explicitly specify binding parameter type:因此,您可以使用EntityManager和休眠特定的setParameter重写您的查询,该setParameter允许显式指定绑定参数类型:

import org.hibernate.type.LongType;

// ...

List<CustomProjection> results = entityManager.createNamedQuery("find_by_custom_condition" )
.unwrap(org.hibernate.query.Query.class)
.setParameter("identifierId", identifierId, LongType.INSTANCE)
.getResultList();

Then you can use @NamedNativeQuery with @SqlResultSetMapping to map native query results to the CustomProjection .然后你可以使用@NamedNativeQuery@SqlResultSetMapping映射本地查询结果的CustomProjection See examples in the hibernate documentation .请参阅休眠文档中的示例。

See also this article about setParameter usage in JPA and Hibernate query.另请参阅有关在 JPA 和 Hibernate 查询中使用setParameter文章

So this is not the exact answer to the question above, but I was facing a similar issue, I figured I would add it here, for those that come across this question.所以这不是上述问题的确切答案,但我遇到了类似的问题,我想我会在这里添加它,对于那些遇到这个问题的人。

I was using a native query where the in clause of the query might be null, ie:我正在使用本机查询,其中查询的 in 子句可能为空,即:

WHERE (cm.first_name in (:firstNames) OR :firstNames is NULL)

I was getting the bytea error, in the end I was able to send an empty list.我收到了bytea错误,最后我能够发送一个空列表。

(null == entity.getFirstName()? Collections.emptyList() : entity.getFirstName())

In this case, sending the empty list to the resolver worked, where as null did not.在这种情况下,将空列表发送到解析器起作用,而 null 则不起作用。

hope this saves some time for people who come across this question looking for how to pass null to a list parameter of a native query.希望这可以为遇到此问题的人节省一些时间,以寻找如何将 null 传递给本机查询的列表参数。

Following code also worked for me when booleanParameterValue is null.当 booleanParameterValue 为 null 时,以下代码也适用于我。 My database is Oracle and, application is using jpql and hibernate.我的数据库是 Oracle,应用程序使用 jpql 和 hibernate。

Query query = getEntityManager().createNamedQuery("findBankAccountInfos");
query.setParameter("booleanParameter", Boolean.FALSE);
query.setParameter("booleanParameter", booleanParameterValue);

Otherwise application is throwing following exception:否则应用程序将引发以下异常:

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: expected BINARY got NUMBER

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM