查询导致“错误:运算符不存在:字符变化~~ bytea”

问题描述

我有一个查询,需要首先检查输入参数是否为null或比较列值以传递输入参数。这意味着列值可以为null或通过指定的条件(?3为null或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 属性中传递的空值失败。 错误

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

我尝试在Google以及Stack Overflow上进行搜索。有很多类似的问题被问及回答。但是这些解决方案都不适合我。

如果有人能提供一些见解或方向,我将非常感谢。

注意:Spring引导版本-2.2.7.RELEASE,使用的Postgresql库版本-42.2.16,使用的Postgresql版本-12.4

解决方法

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

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

Postgres如果为null,则无法确定参数的类型。

问题已在此处讨论: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

,

如果您有可能在本机查询中使用空值,则必须直接使用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,Pageable pageable) {
    EntityManager em = ...get from somewhere (maybe parameter?);
    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,然后告诉它在以后的阶段承担bite[]。这样做是出于与其他数据库的旧兼容性原因,并且不太可能更改。也许新的Hibernate 6.0类型的系统可以解决这个问题,但是我没有跟上。因此,当它告诉Postgres类型为bytea时,查询解析器将找不到在bytea和给定的其他数据库类型之间注册的隐式类型转换器,因此会引发错误。

,

似乎使用命名参数而不是匿名参数使其有效。

就我而言,这不起作用:

@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 WARN 28896 --- [XNIO-1 task-3] o.h.engine.jdbc.spi.SqlExceptionHelper:SQL 错误:0,SQLState: 42883 2021-07-05 10:13:39.768 错误 28896 --- [XNIO-1 task-3] o.h.engine.jdbc.spi.SqlExceptionHelper:错误:运算符不存在:文本~~ 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,boolean waitingList);

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

我使用 PostgreSQL 13。