为什么 "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 中有效,但我无法让它在 JPQLCriteriaQuery 中工作; 所以问题总是出在 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("..")JPQLCriteriaQuery 一起使用以保持数据库独立性。

@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 子句中。