为什么 "ORDER BY field_name LIKE 'start%'" 在 Hibernate/Spring/JPA 中不起作用,而它在普通 SQL 中起作用?
Why is "ORDER BY field_name LIKE 'start%'" not working in Hibernate/Spring/JPA while it does work in plain SQL?
问题:
我有一个查询需要在 ORDER BY 语句中使用 LIKE。
虽然这在普通 SQL 中有效,但我无法让它在 JPQL
或 CriteriaQuery
中工作;
所以问题总是出在 order by LOWER(i.name) like "abc%" desc
!
此查询有效并列出名称包含搜索词的项目。它 returns 首先是名称以搜索词开头的项目,然后是所有其他项目。这是普通的查询 SQL:
SELECT i.name, i.popularity
FROM item i
left join picture p on p.id=i.picture_id and p.disabled=false
left join picture b on p.id=i.background_picture_id and b.disabled=false
WHERE i.disabled=false and LOWER(i.name) like "%abc%"
order by LOWER(i.name) like "abc%" desc, i.popularity desc
limit 50;
虽然这是一个开始,但我更愿意将 JpaRepository
与 @Query("..")
、JPQL
或 CriteriaQuery
一起使用以保持数据库独立性。
@Query("SELECT i FROM ItemEntity i " +
"left join i.picture p on p.disabled=false " +
"left join i.backgroundPicture b on b.disabled=false " +
"WHERE i.disabled=false and LOWER(i.name) like %:partialName% " +
"order by (LOWER(i.name) like :partialNameOrderBy%) desc, i.popularity desc")
Page<ItemListView> findActiveItemsWhereNameContains(@Param("partialName") String partialNameLowerCase, @Param("partialNameOrderBy") String partialNameLowerCaseOrderByStartsWith, Pageable pageable);
此 JPQL 查询结果为:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: like near line 1, column
所以我试了一下 CriteriaQuery
:
@Override
public Page<ItemListView> findActiveItemsWhereNameContains(String partialName, int maxResults) {
String partialNameLowerCase = StringUtils.lowerCase(StringUtils.trimToEmpty(partialName));
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ItemListView> cq = cb.createQuery(ItemListView.class);
Root<ItemEntity> item = cq.from(ItemEntity.class);
Join<ItemEntity, Ct2PictureEntity> picture = item.join(ItemEntity_.picture, JoinType.LEFT);
picture.on(cb.and(picture.getOn()), cb.isFalse(picture.get(Ct2PictureEntity_.disabled)));
Join<ItemEntity, Ct2PictureEntity> backgroundPicture = item.join(ItemEntity_.backgroundPicture, JoinType.LEFT);
picture.on(cb.and(backgroundPicture.getOn()), cb.isFalse(backgroundPicture.get(Ct2PictureEntity_.disabled)));
Predicate itemIsActive = cb.isFalse(item.get(ItemEntity_.disabled));
Predicate itemNameContainsSearch = cb.like(item.get(ItemEntity_.name), "%" + partialNameLowerCase + "%");
Predicate itemNameStartsWithSearch = cb.like(item.get(ItemEntity_.name), partialNameLowerCase + "%");
cq.select(cb.construct(
ItemListViewDto.class,
item.get(ItemEntity_.id),
item.get(ItemEntity_.name),
item.get(ItemEntity_.popularity),
CriteriaBuilderUtils.getMediumAndThumbnailPicture(cb, picture),
CriteriaBuilderUtils.getMediumPicture(cb, backgroundPicture)))
.where(cb.and(itemIsActive, itemNameContainsSearch)).orderBy(cb.desc(itemNameStartsWithSearch), cb.desc(item.get(ItemEntity_.popularity)));
TypedQuery<ItemListView> query = entityManager.createQuery(cq);
Pageable pageable = PageRequest.of(0, maxResults);
int totalRows = query.getResultList().size();
query.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
query.setMaxResults(pageable.getPageSize());
return new PageImpl<>(query.getResultList(), pageable, totalRows);
}
此查询结果:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: like near line 1, column
问题:
如何使用 JPA 或 Hibernate 编写此查询并避免原生 SQL?
这里的问题是 Hibernate 不会将谓词强制转换为布尔表达式。您将不得不使用例如CASE WHEN (LOWER(i.name) like :partialNameOrderBy%) THEN 1 ELSE 0 END
相反,您可以将其放入 order by 子句中。
问题:
我有一个查询需要在 ORDER BY 语句中使用 LIKE。
虽然这在普通 SQL 中有效,但我无法让它在 JPQL
或 CriteriaQuery
中工作;
所以问题总是出在 order by LOWER(i.name) like "abc%" desc
!
此查询有效并列出名称包含搜索词的项目。它 returns 首先是名称以搜索词开头的项目,然后是所有其他项目。这是普通的查询 SQL:
SELECT i.name, i.popularity
FROM item i
left join picture p on p.id=i.picture_id and p.disabled=false
left join picture b on p.id=i.background_picture_id and b.disabled=false
WHERE i.disabled=false and LOWER(i.name) like "%abc%"
order by LOWER(i.name) like "abc%" desc, i.popularity desc
limit 50;
虽然这是一个开始,但我更愿意将 JpaRepository
与 @Query("..")
、JPQL
或 CriteriaQuery
一起使用以保持数据库独立性。
@Query("SELECT i FROM ItemEntity i " +
"left join i.picture p on p.disabled=false " +
"left join i.backgroundPicture b on b.disabled=false " +
"WHERE i.disabled=false and LOWER(i.name) like %:partialName% " +
"order by (LOWER(i.name) like :partialNameOrderBy%) desc, i.popularity desc")
Page<ItemListView> findActiveItemsWhereNameContains(@Param("partialName") String partialNameLowerCase, @Param("partialNameOrderBy") String partialNameLowerCaseOrderByStartsWith, Pageable pageable);
此 JPQL 查询结果为:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: like near line 1, column
所以我试了一下 CriteriaQuery
:
@Override
public Page<ItemListView> findActiveItemsWhereNameContains(String partialName, int maxResults) {
String partialNameLowerCase = StringUtils.lowerCase(StringUtils.trimToEmpty(partialName));
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ItemListView> cq = cb.createQuery(ItemListView.class);
Root<ItemEntity> item = cq.from(ItemEntity.class);
Join<ItemEntity, Ct2PictureEntity> picture = item.join(ItemEntity_.picture, JoinType.LEFT);
picture.on(cb.and(picture.getOn()), cb.isFalse(picture.get(Ct2PictureEntity_.disabled)));
Join<ItemEntity, Ct2PictureEntity> backgroundPicture = item.join(ItemEntity_.backgroundPicture, JoinType.LEFT);
picture.on(cb.and(backgroundPicture.getOn()), cb.isFalse(backgroundPicture.get(Ct2PictureEntity_.disabled)));
Predicate itemIsActive = cb.isFalse(item.get(ItemEntity_.disabled));
Predicate itemNameContainsSearch = cb.like(item.get(ItemEntity_.name), "%" + partialNameLowerCase + "%");
Predicate itemNameStartsWithSearch = cb.like(item.get(ItemEntity_.name), partialNameLowerCase + "%");
cq.select(cb.construct(
ItemListViewDto.class,
item.get(ItemEntity_.id),
item.get(ItemEntity_.name),
item.get(ItemEntity_.popularity),
CriteriaBuilderUtils.getMediumAndThumbnailPicture(cb, picture),
CriteriaBuilderUtils.getMediumPicture(cb, backgroundPicture)))
.where(cb.and(itemIsActive, itemNameContainsSearch)).orderBy(cb.desc(itemNameStartsWithSearch), cb.desc(item.get(ItemEntity_.popularity)));
TypedQuery<ItemListView> query = entityManager.createQuery(cq);
Pageable pageable = PageRequest.of(0, maxResults);
int totalRows = query.getResultList().size();
query.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
query.setMaxResults(pageable.getPageSize());
return new PageImpl<>(query.getResultList(), pageable, totalRows);
}
此查询结果:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: like near line 1, column
问题:
如何使用 JPA 或 Hibernate 编写此查询并避免原生 SQL?
这里的问题是 Hibernate 不会将谓词强制转换为布尔表达式。您将不得不使用例如CASE WHEN (LOWER(i.name) like :partialNameOrderBy%) THEN 1 ELSE 0 END
相反,您可以将其放入 order by 子句中。