更多干货
转载:https://blog.csdn.net/feinifi/article/details/80830406
我们知道hibernate作为orm框架在javaweb中的应用非常广泛,但是hibernate还不仅如此,在hibernate3.2之后还支持全文检索功能,他需要和lucene结合使用。
通常,为了查询一个关键字,我们需要对我们的表查询时做like过滤,但是like只能对一个字段进行,如果要做到多个字段进行匹配,like就显得力不从心了,而且在匹配时数据库效率会大打折扣。
hibernate-search的出现,帮我们解决了上面的问题,我们在做关键字过滤时,可以指定多个属性。hibernate search是基于apache lucene的全文检索工具。
下面通过实例介绍如何通过hibernate search作全文检索。
一、准备数据库表和数据。
向表中插入10条记录,最终数据如下:
构建索引时,我们希望通过name,description字段进行构建,这样,我们在通过关键字查询时,name,description中任意一个出现关键字,那么就可以被查询出来。
二、构建maven项目,引入相关依赖。
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>5.5.5</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<version>5.10.2.Final</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-smartcn</artifactId>
<version>3.6.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
创建实体Book
package com.xxx.hibernate.domain;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;
@Entity
@Table(name="xx_books")
@Indexed
public class Book implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
@Field(index=Index.YES,analyze=Analyze.YES,store=Store.YES)
private String name;
@Field(index=Index.YES,analyze=Analyze.YES,store=Store.YES)
private String description;
private String publish;
private String author;
@Id
@GeneratedValue(generator="increment")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPublish() {
return publish;
}
public void setPublish(String publish) {
this.publish = publish;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", description="
+ description + ", publish=" + publish + ", author=" + author
+ "]";
}
}
需要注意的地方:
实体类配置Indexed注解,表明我们需要对其构建索引。
属性name,description需要通过Field注解,标识是否作为索引,是否分词,是否存储。
配置hibernate-cfg.xml,设置driver,url,username,password,pool_size,show_sql,dialect等属性。另外有两项重要的配置分别是存储实现hibernate.search.default.directory_provider和索引存放位置hibernate.search.default.indexBase。
<session-factory>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test?useSSL=false</property>
<property name="connection.username">root</property>
<property name="connection.password">PWD-pushmarketing2015!</property>
<property name="connection.pool_size">5</property>
<property name="dialect">org.hibernate.dialect.MySQL57Dialect</property>
<property name="show_sql">true</property>
<!-- hibernate search -->
<property name="hibernate.search.default.directory_provider">filesystem</property>
<property name="hibernate.search.default.indexBase">D:/data/indexs</property>
<mapping class="com.xxx.hibernate.domain.Book"/>
</session-factory>
这里,Book实体通过注解指定了表和id增长类型,所以这里就不用再配置xml映射文件。直接通过类来指定。
以上准备好了,就可以编码构建索引和搜索了。
三、编码,构建索引。
初始化SessionFactory,并提供获取Session静态方法getSession()。
private static SessionFactory factory;
private static Session session;
static{
try {
Configuration cfg = new Configuration();
cfg.configure("hibernate-cfg.xml");
factory = cfg.buildSessionFactory();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
private static Session getSession(){
if(session==null){
session = factory.openSession();
}
return session;
}
构建索引方法:
public static void index(){
Session session = getSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
try {
fullTextSession.createIndexer().startAndWait();
System.out.println("create index ok.");
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
factory.close();
}
}
在main方法里面运行index()方法,构建索引。
控制台打印截图:
存放索引的文件夹截图:
到这里,索引创建成功。
四、编码,搜索。
通过第三步,我们已经对Book实体成功做了索引。这里,我们需要对索引做搜索,我们对name,description字段中含有"java"关键字的book进行搜索并打印出来。
@SuppressWarnings("unchecked")
public static void queryIndex(){
Session session = getSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Book.class).get();
Query query = null;
try {
query = qb.keyword().onFields("name","description").matching("action").createQuery();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, Book.class);
List<Book> list = fullTextQuery.list();
System.out.println("query is done.");
for(Book book:list){
System.out.println(book);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
session.close();
factory.close();
}
}
Hibernate: select this_.id as id1_0_0_, this_.author as author2_0_0_, this_.description as descript3_0_0_, this_.name
as name4_0_0_, this_.publish as publish5_0_0_ from xx_books this_ where (this_.id in (?, ?))
query is done.
Book [id=1, name=java program, description=java program for beginners, publish=2018-01-01, author=james]
Book [id=7, name=think in java, description=java program guide, publish=2018-02-01, author=jack]
查询到了java关键字的记录。
这里好像有一个问题,就是javascript也是含有java的,这里没有被查询出来。但是通过sql like查询,java,javascript均能被查询出来。
另外一个问题,如果name,description均不作分词analyze=Analyze.NO,那么他们将作为整体来做索引,就是说如果单独搜索"java",那么就什么都查询不到。