Spring 引导 JPA:为同一参数传递多个值 (JPQL)

Spring Boot JPA: Passing Multiple Values for the Same Parameter (JPQL)

我正在使用 JPQL 在 JPA 的 CurdRepository 界面中编写 SQL 查询。我能够使用命名参数编写多个查询,它们的工作就像一个魅力。

但是,我的应用程序要求我将未知数量的字符串与 table 中的一列以上相匹配。据我所知,在 SQL 中它看起来像这样:

SELECT * FROM TABLE WHERE COLUMN_1 LIKE "%string_1%" OR COLUMN_1 LIKE"%string_2%" ... OR COLUMN_2 LIKE "%string_1%" OR COLUMN_2 LIKE "%string_2%" ... ...

我在 MySQL 中尝试过,效果很好。但是,我不知道如何在 JPQL 中描述它。通过将命名参数设置为 List,我能够为 IN 子句传递多个值,如下所示:

 @Query("SELECT t FROM Table t WHERE TABLE_ID IN (:table_ids)")
public Page<Table> findByParameters(@Param("table_ids") List<Integer> table_ids, Pageable page)

我尝试用 LIKE 子句做同样的事情,但如果我传递超过 1 个值,我最终得到 0 个结果。 LOCATE 子句也面临同样的问题。

最坏的情况,我可以设置一个适用于一个字符串的查询,并多次调用它,然后合并所有结果。不过,我宁愿避免这种情况,因为它只不过是一种胶带解决方案。

我在网上查找了几个小时的解决方案,但没有找到任何解决我的问题的方法。非常感谢任何提示。

谢谢。

因为 @Query 需要在编译时使用 JPQL 进行修复,甚至本机查询会使此类事情难以实现,尤其是在类型安全的方式中。

所以我知道您正在寻求 JPQL 解决方案,但这是学习和利用 Specification 接口和 JPA 的 CriteriaQuery 的绝佳机会。这正是它的用途。

查看以下存储库:

public interface Table1Repository            // to use specifications in queries 
        extends JpaRepository<Table1, Long>, JpaSpecificationExecutor<Table1> {

    @SuppressWarnings("serial")
    public static Specification<Table1> multiLikeColumn1(List<String> likePatterns) {
        return new Specification<Table1>() {
            @Override
            public Predicate toPredicate(Root<Table1> root, CriteriaQuery<?> query,
                                            CriteriaBuilder criteriaBuilder) {
                Path<String> column1 = root.get("column1");
                // create a Predicate for each "column1 like 'xy%az%' you need
                List<Predicate> predicates = likePatterns.stream()
                    .map(likePattern -> criteriaBuilder.like(column1, likePattern))
                    .collect(Collectors.toList());
                // then "concatenate" list of likes with "OR"
                return criteriaBuilder.or(predicates.toArray(new Predicate[]{}));
            }
        };
    }

}

它可能看起来有点复杂,但当你熟悉它时实际上并不复杂。用法很简单,如:

@Resource
private Table1Repository repo;
repo.findAll(Table1Repository.multiLikeColumn1(Arrays.asList("%X%","%Z%")))

这对我有用:

1)

@Repository
public interface PostRepository extends PagingAndSortingRepository<Post,Long>, JpaSpecificationExecutor<Post> {
  1. public class PostSpecification {
          public static Specification<Post> textContains(String searchWord) {
              return (root, query, builder) -> {
                  Expression<String> searchLowerCase = builder.lower(root.get("text"));
                  return builder.like(searchLowerCase, "%" + searchWord.toLowerCase() + "%");
              };
          }
      }
    
public Page<Post> getPostsWhereTextContainsAnyWord(List<String> words, int offset, int limit) {
        Specification<Post> specification = null;
        for(String word : words) {
            Specification<Post> wordSpecification = PostSpecification.textContains(word);
            if(specification == null) {
                specification = wordSpecification;
            } else {
                specification = specification.or(wordSpecification);
            }
        }
        return postRepository.findAll(specification, PageRequest.of(offset, limit));
    }