问题描述
我有一个查询,需要首先检查输入参数是否为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);
}
错误:运算符不存在:字符变化~~ 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。