1. 概述
本文将带你快速掌握 Hibernate Search 的核心概念、配置方法,并实现几种常用查询。如果你已经在项目中使用 Hibernate/JPA,那么集成全文搜索功能就差这一步了。
2. Hibernate Search 基础
当需要实现全文搜索时,基于现有技术栈扩展总是最省心的方案。如果你已经在用 Hibernate 和 JPA 做 ORM,那么离 Hibernate Search 仅一步之遥。
Hibernate Search 整合了 Apache Lucene——一个高性能、可扩展的 Java 全文搜索引擎库。这相当于把 Lucene 的强大能力与 Hibernate/JPA 的简洁性完美结合。
简单来说,只需在实体类上加几个注解,数据库和索引的同步工作就全交给它了。Hibernate Search 也支持 Elasticsearch 集成,但目前仍处于实验阶段,本文聚焦 Lucene 实现。
3. 配置指南
3.1. Maven 依赖
先在 pom.xml 添加必要依赖:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<version>5.8.2.Final</version>
</dependency>
为简化演示,我们选用 H2 数据库:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
3.2. 核心配置
必须指定 Lucene 索引的存储位置,通过以下属性配置:
hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase = /data/index/default
这里选择 filesystem
是最直接的方案。集群环境可考虑 filesystem-master
/filesystem-slave
或 infinispan
实现节点间索引同步(详见官方文档)。
4. 实体类设计
配置完成后,开始定义模型类。在 JPA 注解基础上添加 @Indexed
,标记 Product
实体需要被索引。通过 @Field
注解声明可搜索字段:
@Entity
@Indexed
@Table(name = "product")
public class Product {
@Id
private int id;
@Field(termVector = TermVector.YES)
private String productName;
@Field(termVector = TermVector.YES)
private String description;
@Field
private int memory;
// getters, setters, and constructors
}
⚠️ termVector = TermVector.YES
是后续 "More Like This" 查询的必备条件。
5. 构建 Lucene 索引
执行查询前,必须先触发 Lucene 初始索引构建:
FullTextEntityManager fullTextEntityManager
= Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.createIndexer().startAndWait();
初始构建后,Hibernate Search 会自动维护索引同步。后续通过 EntityManager
进行的增删改操作都会自动更新索引。
⚠️ 踩坑提醒:实体必须完全提交到数据库后才能被 Lucene 发现并索引。这就是为什么测试用例中数据导入通常放在单独的 @Commit
注解测试方法里。
6. 构建与执行查询
6.1. 查询执行四步法
所有查询都遵循这个标准流程:
步骤 1:获取 FullTextEntityManager
和 QueryBuilder
:
FullTextEntityManager fullTextEntityManager
= Search.getFullTextEntityManager(entityManager);
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder()
.forEntity(Product.class)
.get();
步骤 2:用 Hibernate 查询 DSL 创建 Lucene 查询:
org.apache.lucene.search.Query query = queryBuilder
.keyword()
.onField("productName")
.matching("iphone")
.createQuery();
步骤 3:包装成 Hibernate 查询:
org.hibernate.search.jpa.FullTextQuery jpaQuery
= fullTextEntityManager.createFullTextQuery(query, Product.class);
步骤 4:执行查询:
List<Product> results = jpaQuery.getResultList();
默认按相关性排序。后续重点介绍步骤 2 的不同查询类型。
6.2. 关键词查询
最基础的精确单词搜索:
Query keywordQuery = queryBuilder
.keyword()
.onField("productName")
.matching("iphone")
.createQuery();
keyword()
:指定精确匹配onField()
:指定搜索字段matching()
:指定搜索内容
6.3. 模糊查询
类似关键词查询,但可定义模糊容差:
Query fuzzyQuery = queryBuilder
.keyword()
.fuzzy()
.withEditDistanceUpTo(2)
.withPrefixLength(0)
.onField("productName")
.matching("iPhaen")
.createQuery();
withEditDistanceUpTo(2)
:允许 2 个字符偏差(Lucene 限制最大 2)withPrefixLength(0)
:前缀 0 个字符参与模糊匹配
6.4. 通配符查询
支持 ?
(单字符)和 *
(多字符)通配符:
Query wildcardQuery = queryBuilder
.keyword()
.wildcard()
.onField("productName")
.matching("Z*")
.createQuery();
6.5. 短语查询
多词搜索,支持精确或近似匹配:
Query phraseQuery = queryBuilder
.phrase()
.withSlop(1)
.onField("description")
.sentence("with wireless charging")
.createQuery();
withSlop(1)
:允许短语间插入 1 个其他词
6.6. 简单字符串查询
让用户自定义查询语法,支持:
- 布尔运算(
+
AND,|
OR,-
NOT) - 前缀查询(
prefix*
) - 短语查询(
"some phrase"
) - 优先级(括号)
- 模糊查询(
fuzy~2
) - 近似短语(
"some phrase"~3
)
示例组合查询:
Query simpleQueryStringQuery = queryBuilder
.simpleQueryString()
.onFields("productName", "description")
.matching("Aple~2 + \"iPhone X\" + (256 | 128)")
.createQuery();
6.7. 范围查询
在指定范围内搜索,支持数字、日期、字符串:
Query rangeQuery = queryBuilder
.range()
.onField("memory")
.from(64).to(256)
.createQuery();
6.8. 相似查询
根据给定实体返回相似结果列表,并附带相似度评分:
Query moreLikeThisQuery = queryBuilder
.moreLikeThis()
.comparingField("productName").boostedTo(10f)
.andField("description").boostedTo(1f)
.toEntity(entity)
.createQuery();
List<Object[]> results = (List<Object[]>) fullTextEntityManager
.createFullTextQuery(moreLikeThisQuery, Product.class)
.setProjection(ProjectionConstants.THIS, ProjectionConstants.SCORE)
.getResultList();
boostedTo()
用于调整字段权重,termVector = TermVector.YES
是前提条件。
6.9. 多字段搜索
同时搜索多个字段:
Query luceneQuery = queryBuilder
.keyword()
.onFields("productName", "description")
.matching(text)
.createQuery();
或分别指定字段并设置权重:
Query moreLikeThisQuery = queryBuilder
.moreLikeThis()
.comparingField("productName").boostedTo(10f)
.andField("description").boostedTo(1f)
.toEntity(entity)
.createQuery();
6.10. 组合查询
通过布尔策略组合查询:
SHOULD
:类似 OR,匹配任一子查询即可MUST
:类似 AND,必须匹配所有子查询MUST NOT
:类似 NOT,不能匹配子查询
组合策略会影响相关性评分,例如两个
SHOULD
查询都匹配时,相关性更高。
Query combinedQuery = queryBuilder
.bool()
.must(queryBuilder.keyword()
.onField("productName").matching("apple")
.createQuery())
.must(queryBuilder.range()
.onField("memory").from(64).to(256)
.createQuery())
.should(queryBuilder.phrase()
.onField("description").sentence("face id")
.createQuery())
.must(queryBuilder.keyword()
.onField("productName").matching("samsung")
.createQuery())
.not()
.createQuery();
7. 总结
本文介绍了 Hibernate Search 的核心概念和主要查询类型实现。更高级的特性可参考官方文档。
完整示例代码见 GitHub 仓库。