You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JPA/Hibernate的ProductRepository中查询含多关键词的产品列表

嘿,我来帮你搞定这个多关键词的模糊查询问题!你当前的代码参数是字符串数组,但SQL里只用到了单个关键词的条件,所以得调整查询逻辑来支持多个关键词的OR匹配。这里有几种实用的实现方式,你可以根据场景选择:

解决方案:多关键词模糊查询的几种实现方式

方法1:原生SQL + SpEL动态拼接条件

如果你习惯写原生SQL,可以用Spring EL(SpEL)动态生成每个关键词的LIKE条件,这样能直接复用你熟悉的SQL写法:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

    @Query(value = "SELECT * FROM product WHERE " +
           "(" +
           "   :#{#words == null || #words.length == 0 ? '1=1' : " +
           "       '(' + T(java.util.stream.Stream).of(#words)" +
           "           .map(w -> 'name LIKE CONCAT(\\'%\\', ?1, \\'%\\') OR description LIKE CONCAT(\\'%\\', ?1, \\'%\\')')" +
           "           .collect(T(java.util.stream.Collectors).joining(' OR ')) + ')'" +
           ")",
           nativeQuery = true)
    Set<Product> findProductsByGivenWords(String[] words);
}

⚠️ 注意:如果关键词来自用户输入,要提前处理SQL通配符(比如%、_)的转义,避免意外匹配和SQL注入风险。

方法2:JPQL集合匹配(更安全)

JPQL对集合参数的支持更友好,用这种方式可以避免手动拼接SQL的麻烦,还天然防注入:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

    @Query("SELECT p FROM Product p WHERE " +
           "(" +
           "   :words IS NULL OR :words IS EMPTY OR " +
           "   EXISTS (" +
           "       SELECT 1 FROM String word IN :words " +
           "       WHERE p.name LIKE CONCAT('%', word, '%') OR p.description LIKE CONCAT('%', word, '%')" +
           "   )" +
           ")")
    Set<Product> findProductsByGivenWords(@Param("words") List<String> words);
}

这里把参数改成List<String>更符合JPA的集合处理习惯,框架会自动帮你生成对应的条件语句。

方法3:Specification动态构建(最灵活)

如果你的查询逻辑可能后续有变化,用Specification动态构建查询会更灵活,完全用Java代码控制查询规则:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {

    default Set<Product> findProductsByGivenWords(String[] words) {
        if (words == null || words.length == 0) {
            return new HashSet<>(); // 这里可以返回空集合或者全量数据,按需调整
        }
        Specification<Product> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            for (String word : words) {
                // 处理通配符转义,避免意外匹配
                String escapedWord = word.replace("%", "\\%").replace("_", "\\_");
                Predicate nameLike = cb.like(root.get("name"), "%" + escapedWord + "%");
                Predicate descLike = cb.like(root.get("description"), "%" + escapedWord + "%");
                predicates.add(cb.or(nameLike, descLike));
            }
            return cb.or(predicates.toArray(new Predicate[0]));
        };
        return new HashSet<>(findAll(spec));
    }
}

这种方式不需要写任何SQL/JPQL,所有查询逻辑都在代码里,维护起来很方便,而且自带SQL注入防护。

额外小贴士

  • 要是name和description字段的数据量很大,建议给这两个字段添加全文索引,能大幅提升模糊查询的性能。
  • 记得处理关键词里的通配符,比如把%换成\%,_换成\_,不然可能会出现不符合预期的匹配结果。

内容的提问来源于stack exchange,提问作者Jonathan Patrascu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:35:22