使用 @Query JPA 注释将空参数传递给本机查询

Posted

技术标签:

【中文标题】使用 @Query JPA 注释将空参数传递给本机查询【英文标题】:Passing a null parameter to a native query using @Query JPA annotation 【发布时间】:2017-08-06 08:58:53 【问题描述】:

在 Spring Boot 应用程序中,我有一个在 postgresql 服务器上执行的 SQL 查询,如下所示:

@Query(value = "select count(*) from servers where brand= coalesce(?1, brand) " +
        "and flavour= coalesce(?2, flavour) ; ",
        nativeQuery = true)
Integer icecreamStockCount(String country, String category);

然而,

执行该方法时出现以下错误:

ERROR: COALESCE types bytea and character varying in PostgreSQL

如何将 String value = null 传递给查询?

**注意:** 我发现我的问题与 JPA Query to handle NULL parameter value 不同

【问题讨论】:

我不知道这是否可能。如果某些输入为空,我会尝试创建单独的查询。 FWIW 没有 JPA @Query 注释。那是春天。 你可以把coalesce改成case when then else end,比如"select count(*) from servers where brand = (case when ?1 = 'undefined' then brand else ?1::bytea) " 【参考方案1】:

你不需要合并,试试这个

@Query("select count(*) from servers where (brand = ?1 or ?1 is null)" +
        " and (flavour = ?2 or ?2 is null)")
Integer icecreamStockCount(String country, String category);

【讨论】:

@Jodiug,你检查了吗?这是 TS 自带的问题。 删除了我的评论。从我的小实验看来,只要只有一个子句使用数据库中的数据,OR 就可以执行。所以?1 = 3 OR x = ?1 可以被索引但?1 = x OR x = 22 不能被索引。因此,您的答案根据该规则起作用。我的错!【参考方案2】:

Hibernate 5.0.2 之前不允许空参数。 见https://hibernate.atlassian.net/browse/HHH-9165 以及对https://www.postgresql.org/message-id/6ekbd7dm4d6su5b9i4hsf92ibv4j76n51f@4ax.com的回复

【讨论】:

【参考方案3】:

如果你真的需要使用native query,就会出现问题,因为它是一项尚未实现的改进,请参阅hibernate。如果您不需要使用 native,您可以使用 (where ?1 is null or field like ?1)。假设您确实需要 native, 您可以通过将其设置为空然后调用存储库来处理字符串,这将是这样的:

@Query(value = "select count(*) from servers where (?1 like '' or brand like ?1) " +
    "and (?2 like '' or flavour like ?2)",
    nativeQuery = true)
Integer icecreamStockCount(String country, String category);

对于本机查询情况,总是有 javax.persistence.EntityManager bean 作为选项,我推荐它而不是以前的方法。在这里,您可以按照您想要的方式附加到您的查询中,如下所示:

String queryString = "select count(*) from servers ";
if (!isNull(country)) queryString += "where brand like :country";
Query query = entityManager.createNativeQuery(queryString);
if (!isNull(country)) query.setParameter("country", country);
return query.getResultList();

观察:

    较新的版本改进了这种“+”连接字符串。但是您可以使用 StringBuilder 或 String Format 以您想要的方式构建您的 queryString,没关系。 小心SQL注入,setParameter方法避免了这种问题,更多信息见Sql Injection Baeldung

【讨论】:

【参考方案4】:

当我遇到此错误时,我最终使用ORCAST 的组合来解决问题。

SELECT COUNT(*)
FROM servers
WHERE (?1 IS NULL OR brand = CAST(?1 AS CHARACTER VARYING))
AND (?2 IS NULL OR flavour = CAST(?2 AS CHARACTER VARYING))

即使 ?1?2brandflavor 都是可为空的字段,这仍然有效。

请注意,将null 传递给?1 的意思是“所有服务器,不分品牌”,而不是“所有不带品牌的服务器”。对于后者,您可以使用IS DISTINCT FROM,如下所示。

SELECT COUNT(*)
FROM servers
WHERE (CAST(?1 AS CHARACTER VARYING) IS NOT DISTINCT FROM brand)
AND (CAST(?2 AS CHARACTER VARYING) IS NOT DISTINCT FROM flavour)

最后,某些参数类型如Boolean不能在SQL中从BYTEA转换为BOOLEAN,对于这些情况你需要双重转换:

SELECT COUNT(*)
FROM servers
WHERE (?1 IS NULL OR is_black = CAST(CAST(?1 AS CHARACTER VARYING) AS BOOLEAN))

在我看来,这是 Hibernate 中的一个问题,可以通过将 Java null 参数作为普通 SQL NULLs 传递而不是将 null 解释为 BYTEA 类型的值来解决。

【讨论】:

【参考方案5】:

所以这不是上述问题的确切答案,但我遇到了类似的问题,我想我会在这里添加它,对于那些遇到这个问题的人。

我使用的是本机查询,就我而言,它不是像上面那样的奇异值,但我传入了一个列表来匹配查询的这一部分:

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

我收到了bytea 错误,最后我能够发送一个空列表。

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

在这种情况下,将空列表发送到解析器是可行的,而 null 则没有。

希望这可以为您节省一些时间。

【讨论】:

以上是关于使用 @Query JPA 注释将空参数传递给本机查询的主要内容,如果未能解决你的问题,请参考以下文章

将空值作为参数传递给方法时如何修复 NullPointerException [重复]

如何将空参数传递给 msdeploy powershell deploy 命令

Spring Data JPA如何传递日期参数

尝试使用 WebClient 将空正文传递给 POST 端点

Spring Data JPA - 将列名和值作为参数传递

为啥threading.thread将空字典传递给python中的函数线程