查询结果为 “ERROR: operator does not exist: character varying ~~ bytea”

huangapple go评论73阅读模式
英文:

Query resulting into "ERROR: operator does not exist: character varying ~~ bytea"

问题

我有一个查询,在这个查询中,我首先需要检查输入参数是否为空,或者比较列的值以传递输入参数。这意味着列的值可以是空的,或者满足指定的条件 (?3 为空或 cd.name 类似于 %?3%)

public interface PageableCategoryRepository extends PagingAndSortingRepository<Category, Long> {
  @Query(
      value = "select distinct c from Category c left join fetch c.descriptions cd join fetch cd.language cdl join fetch c.merchantStore cm"
          + "  where cm.id=?1 and cdl.id=?2 and (?3 is null or cd.name like %?3%) order by c.lineage, c.sortOrder asc",
      countQuery = "select  count(c) from Category c join c.descriptions cd join c.merchantStore cm "
          + "where cm.id=?1 and cd.language.id=?2 and (?3 is null or cd.name like %?3%)")
  Page<Category> listByStore(Integer storeId, Integer languageId, String name, Pageable pageable);
}

上面的查询在传递空值到 name 属性时失败。
错误:

错误:运算符不存在:character varying ~~ bytea
提示:没有与给定名称和参数类型匹配的运算符。您可能需要添加显式类型转换。
位置:3259

我尝试在Google上搜索,以及在这里查找Stack Overflow上的解答。有许多类似的问题被提问和回答过。但是这些解决方案都不适用于我。

如果有人能够提供一些见解或指导,我会非常感谢。

注:Spring Boot 版本 - 2.2.7.RELEASE,PostgreSQL 库版本 - 42.2.16,PostgreSQL 版本 - 12.4

英文:

I have a query where I need to check first if the input parameter is null or compare the column value to pass input parameter. It means that the column value can be null or pass the specified condition (?3 is null or cd.name like %?3%).

public interface PageableCategoryRepository extends PagingAndSortingRepository&lt;Category, Long&gt; {
  @Query(
      value = &quot;select distinct c from Category c left join fetch c.descriptions cd join fetch cd.language cdl join fetch c.merchantStore cm&quot;
          + &quot;  where cm.id=?1 and cdl.id=?2 and (?3 is null or cd.name like %?3%) order by c.lineage, c.sortOrder asc&quot;,
      countQuery = &quot;select  count(c) from Category c join c.descriptions cd join c.merchantStore cm &quot;
          + &quot;where cm.id=?1 and cd.language.id=?2 and (?3 is null or cd.name like %?3%)&quot;)
  Page&lt;Category&gt; listByStore(Integer storeId, Integer languageId, String name, Pageable pageable);
}

The above query is failing for the null value passed in name property.
Error:

> ERROR: operator does not exist: character varying ~~ bytea
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Position: 3259

I tried to search on google as well as here on Stack Overflow. There are many similar questions asked & answered. But none of those solutions work for me.

Would really appreciate if anybody can provide some insight or direction.

Note: Spring boot version- 2.2.7.RELEASE, Postgresql library version used- 42.2.16, Postgresql version used- 12.4

答案1

得分: 3

Postgres在参数为null时无法确定其类型。

问题已在此讨论:https://stackoverflow.com/questions/35571624/spring-data-rest-date-is-null-query-throws-an-postgres-exception

建议的解决方案是显式地对参数进行转换(如错误消息中也建议的),或者将参数包装在coalesce语句中。
所以可以这样做:
将所有这些:

?3 is null 

替换为这个语句:

coalesce(?3, null) is null

当涉及到参数变化的查询时,建议查看Criteria API,而不是使用@Query,因为它允许动态创建查询:
https://www.baeldung.com/hibernate-criteria-queries

英文:

Postgres cannot determine the type of the parameter if it is null.

The problem has been discussed here: https://stackoverflow.com/questions/35571624/spring-data-rest-date-is-null-query-throws-an-postgres-exception

The suggested solutions were to explicitly cast the parameter (like also suggested in the error message), or to wrap the parameter in a coalesce statement.
So this should to the trick:
Replace all of these:

?3 is null 

by this statement:

coalesce(?3, null) is null

When it comes to queries where the parameters vary, it is also a good idea to have a look at the Criteria API instead of using @Query as it allows to create queries very dynamically:
https://www.baeldung.com/hibernate-criteria-queries

答案2

得分: 2

似乎使用命名参数而不是匿名参数可以使其正常工作。

在我的情况下,这是不起作用的:

@Query("""
	SELECT p FROM Participant p
	WHERE (?1 IS NULL OR p.firstName LIKE ?1)
	AND ?2 IS NULL OR e.id = ?2
	AND p.waitingList = ?3
	""")
List<Participant> findFiltered(String searchCriteria, Long eventId, boolean waitingList);

> 2021-07-05 10:13:39.768 警告 28896 --- [ XNIO-1 task-3]
> o.h.engine.jdbc.spi.SqlExceptionHelper : SQL 错误: 0,SQL 状态:
> 42883 2021-07-05 10:13:39.768 错误 28896 --- [ XNIO-1 task-3]
> o.h.engine.jdbc.spi.SqlExceptionHelper : 错误:操作符不存在:text ~~ bytea 提示:没有匹配给定名称和参数类型的操作符。您可能需要添加显式类型转换。 位置:951

但是使用命名参数,它可以正常工作:

@Query("""
	SELECT p FROM Participant p
	WHERE (:searchCriteria IS NULL OR p.firstName LIKE :searchCriteria)
	AND :eventId IS NULL OR e.id = :eventId
	AND p.waitingList = :waitingList
	""")
List<Participant> findFiltered(@Param("searchCriteria") String searchCriteria, @Param("eventId") Long eventId, @Param("waitingList") boolean waitingList);

此外,如错误消息所述,显式转换也可以正常工作:

@Query("""
	SELECT p FROM Participant p
	WHERE (cast(?1 as text) IS NULL OR p.firstName LIKE cast(?1 as text))
	AND cast(?2 as long) IS NULL OR e.id = cast(?2 as long)
	AND p.waitingList = ?3
	""")
List<Participant> findFiltered(String searchCriteria, Long eventId, boolean waitingList);

有关可用的转换类型,请参阅 https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#basic-provided

我使用的是 PostgreSQL 13。

英文:

It seems that using named parameters instead of anonymous parameters make it works.

In my case, this was not working :

@Query(&quot;&quot;&quot;
	SELECT p FROM Participant p
	WHERE (?1 IS NULL OR p.firstName LIKE ?1)
	AND ?2 IS NULL OR e.id = ?2
	AND p.waitingList = ?3
	&quot;&quot;&quot;)
List&lt;Participant&gt; findFiltered(String searchCriteria, Long eventId, boolean waitingList);

> 2021-07-05 10:13:39.768 WARN 28896 --- [ XNIO-1 task-3]
> o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState:
> 42883 2021-07-05 10:13:39.768 ERROR 28896 --- [ XNIO-1 task-3]
> o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: operator does not exist: text ~~ bytea Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts. Position : 951

But using named parameters, it works :

@Query(&quot;&quot;&quot;
	SELECT p FROM Participant p
	WHERE (:searchCriteria IS NULL OR p.firstName LIKE :searchCriteria)
	AND :eventId IS NULL OR e.id = :eventId
	AND p.waitingList = :waitingList
	&quot;&quot;&quot;)
List&lt;Participant&gt; findFiltered(@Param(&quot;searchCriteria&quot;) String searchCriteria, @Param(&quot;eventId&quot;) Long eventId, @Param(&quot;waitingList&quot;) boolean waitingList);

Else, as stated by the error message, explicit cast also works fine :

@Query(&quot;&quot;&quot;
	SELECT p FROM Participant p
	WHERE (cast(?1 as text) IS NULL OR p.firstName LIKE cast(?1 as text))
	AND cast(?2 as long) IS NULL OR e.id = cast(?2 as long)
	AND p.waitingList = ?3
	&quot;&quot;&quot;)
List&lt;Participant&gt; findFiltered(String searchCriteria, Long eventId, boolean waitingList);

For available cast types refer to https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#basic-provided

I use PostgreSQL 13.

答案3

得分: 1

如果您的本地查询可能出现空值(null values),则必须直接使用JPA接口,而不是让Spring为您调用它们。而不是:

@Query(
    value = "select distinct c from Category c left join fetch c.descriptions cd join fetch cd.language cdl join fetch c.merchantStore cm"
        + "  where cm.id=?1 and cdl.id=?2 and (?3 is null or cd.name like %?3%) order by c.lineage, c.sortOrder asc",
    countQuery = "select  count(c) from Category c join c.descriptions cd join c.merchantStore cm "
        + "where cm.id=?1 and cd.language.id=?2 and (?3 is null or cd.name like %?3%)")
Page<Category> listByStore(Integer storeId, Integer languageId, String name, Pageable pageable);

您需要:

Page<Category> listByStore(Integer storeId, Integer languageId, String name, Pageable pageable) {
    EntityManager em = ...从某处获取也许是参数?);
    TypedQuery<Category> q = (TypedQuery<Category>) em.createNativeQuery(..., Category.class);
    Integer exampleInt = 0;
    String exampleString = "";
    q.setParameter(1, exampleInt).setParameter(1, storeId);
    q.setParameter(2, exampleInt).setParameter(2, languageId);
    q.setParameter(3, exampleString).setParameter(3, name);
}

第一次调用 setParameter 告诉它类型,第二次调用设置实际值。

这背后的原因是Postgres在解析时间内确定类型,而Hibernate无法确定null的类型,因此在某个阶段它被假定为java.io.Serializable,然后在稍后的阶段告诉它假定为byte[]。出于与其他数据库的遗留兼容性原因,这样做是合理的,而且不太可能改变。也许新的Hibernate 6.0类型系统会解决这个问题,但我没有跟上相关动态。因此,当它告诉Postgres类型为bytea时,查询解析器无法找到在bytea和给定的其他数据库类型之间注册的隐式类型转换器,因此会引发错误。

英文:

If you have the potential for null values being used in a native query, then you have to use the JPA interfaces directly, instead of having Spring call them for you. Instead of:

  @Query(
      value = &quot;select distinct c from Category c left join fetch c.descriptions cd join fetch cd.language cdl join fetch c.merchantStore cm&quot;
          + &quot;  where cm.id=?1 and cdl.id=?2 and (?3 is null or cd.name like %?3%) order by c.lineage, c.sortOrder asc&quot;,
      countQuery = &quot;select  count(c) from Category c join c.descriptions cd join c.merchantStore cm &quot;
          + &quot;where cm.id=?1 and cd.language.id=?2 and (?3 is null or cd.name like %?3%)&quot;)
  Page&lt;Category&gt; listByStore(Integer storeId, Integer languageId, String name, Pageable pageable);

you need:

Page&lt;Category&gt; listByStore(Integer storeId, Integer languageId, String name, Pageable pageable) {
    EntityManager em = ...get from somewhere (maybe parameter?);
    TypedQuery&lt;Category&gt; q = (TypedQuery&lt;Category&gt;) em.createNativeQuery(..., Category.class);
    Integer exampleInt = 0;
    String exampleString = &quot;&quot;;
    q.setParameter(1, exampleInt).setParameter(1, storeId);
    q.setParameter(2, exampleInt).setParameter(2, languageId);
    q.setParameter(3, exampleString).setParameter(3, name);
}

The first call to setParameter tells it the type, the second one sets the real value.

The reason behind this is that Postgres determines types during parse time, and Hibernate cannot determine the type of null, so it is assumed to be a java.io.Serializable at one stage, and which then tells it to assume bite[] at a later stage. This is done for legacy compatibility reasons with other databases, and is unlikely to change. Maybe the new Hibernate 6.0 type system will address it, but I haven't kept up. So then when it tells Postgres that the type is bytea, the query parser can't find an implicit type converter registered between bytea and the given other database type, so it throws an error.

答案4

得分: 1

最简单的解决方案是使用显式的类型转换。此外,“LIKE”的右参数必须是一个字符串,因此要用单引号括起来:

WHERE ... (?3::text IS NULL
           OR cd.name::text LIKE '%%' || ?3::text || '%%'
          )
英文:

The easiest solution is to use explicit type casts. Moreover, the right argument of LIKE must be a string, so enclosed in single quotes:

WHERE ... (?3::text IS NULL
           OR cd.name::text LIKE &#39;%&#39; || ?3::text || &#39;%&#39;
          )

huangapple
  • 本文由 发表于 2020年9月20日 00:40:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/63971110.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定