Hibernate Search
也许你的项目正在使用Spring+Hibernate,而你正好需要建立全文检索。Hibernate search可以无缝得整合Hibernate和Lucene,帮助你快速实现功能强大的全文检索。
Hibernate Search通过整合Hibernate core和Lucene建立持久化对象的索引。功能强大而配置简单。下载请到官方网站。
Hibernate Search运行的环境如下:
1、JDK或JRE 5.0以上
2、Hibernate-Search以及相应的依赖包
3、Hibernate Core 3.2.X
4、Hibernate Annotations 3.3.X
首先将下载到的JAR包添加到你的项目中。
新建一个领域模型
public class Book { Integer bookId; String title; String author; String publishDate; String summarize; public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getPublishDate() { return publishDate; } public void setPublishDate(String publishDate) { this.publishDate = publishDate; } public String getSummarize() { return summarize; } public void setSummarize(String summarize) { this.summarize = summarize; } }
public class Book { Integer bookId; String title; String author; String publishDate; String summarize; public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getPublishDate() { return publishDate; } public void setPublishDate(String publishDate) { this.publishDate = publishDate; } public String getSummarize() { return summarize; } public void setSummarize(String summarize) { this.summarize = summarize; } }
接下来在类中加入Hibernate search注解
//索引名称 @Indexed(index = "books") //分词器 @Analyzer(impl = MMSegAnalyzer.class) public class Book { Integer bookId; String title; String author; String publishDate; String summarize; //以对象的唯一标志做为索引中文档的唯一标志 @DocumentId public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } //索引title字段,并设置了权重 @Field(name = "title", index = Index.TOKENIZED, store = Store.YES, boost=@Boost(2f)) @Boost(1.5f) public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getPublishDate() { return publishDate; } public void setPublishDate(String publishDate) { this.publishDate = publishDate; } //索引摘要字段,并设置权重 @Field(name = "summarize", index = Index.TOKENIZED, store = Store.YES, boost=@Boost(1.2f)) public String getSummarize() { return summarize; } public void setSummarize(String summarize) { this.summarize = summarize; }
//索引名称 @Indexed(index = "books") //分词器 @Analyzer(impl = MMSegAnalyzer.class) public class Book { Integer bookId; String title; String author; String publishDate; String summarize; //以对象的唯一标志做为索引中文档的唯一标志 @DocumentId public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } //索引title字段,并设置了权重 @Field(name = "title", index = Index.TOKENIZED, store = Store.YES, boost=@Boost(2f)) @Boost(1.5f) public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getPublishDate() { return publishDate; } public void setPublishDate(String publishDate) { this.publishDate = publishDate; } //索引摘要字段,并设置权重 @Field(name = "summarize", index = Index.TOKENIZED, store = Store.YES, boost=@Boost(1.2f)) public String getSummarize() { return summarize; } public void setSummarize(String summarize) { this.summarize = summarize; } }
本文没有使用注解来完成类和数据的映射,而是使用book.hbm.xml的映射文件,当然你不用对映射文件做任何改动。如果你想使用注解,直接在类里中加入注解即可。
接下来需要修改hibernate.cfg.xml。在sessionFactory节中加入
<property name="hibernate.search.default.directory_provider"> org.hibernate.search.store.FSDirectoryProvider </property> <property name="hibernate.search.default.indexBase"> D:\data\indexs </property>
<property name="hibernate.search.default.directory_provider"> org.hibernate.search.store.FSDirectoryProvider </property> <property name="hibernate.search.default.indexBase"> D:\data\indexs </property>
到这里,配置和修改就已经完成了。当对象被插入,修改或删除的时候,将会出发索引的修改。
如果你想对数据库中已有的数据建立索引可以
public class IndexBuilder { static Logger log = Logger.getLogger("IndexBuilder"); public static void main(String[] args) { Session session = null; try { //配置文件 ApplicationContext cxt = new FileSystemXmlApplicationContext( "..\\WEB-INF\\applicationContext.xml"); SessionFactory sessionFactory = (SessionFactory) cxt .getBean("sessionFactory"); session = sessionFactory.openSession(); FullTextSession fullTextSession = Search .createFullTextSession(session); Query query = session.createQuery("from Book") .setMaxResults(1000); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); int j = 0; int sum = 0; log.info("start Indexing at "+df.format(Calendar.getInstance().getTimeInMillis())); while (true) { session.getTransaction().begin(); List<PvmsMaterialProgram> list = query.setFirstResult(j).list(); for (PvmsMaterialProgram program : list) { fullTextSession.index(program); } session.getTransaction().commit(); j += 1000; if (list.size() < 1000) break; sum += list.size(); log.info("finish " + sum + " records"); } log.info("end at "+df.format(Calendar.getInstance().getTimeInMillis())); } catch (Exception e) { e.printStackTrace(); } finally { if (session != null) session.close(); } } }
public class IndexBuilder { static Logger log = Logger.getLogger("IndexBuilder"); public static void main(String[] args) { Session session = null; try { //配置文件 ApplicationContext cxt = new FileSystemXmlApplicationContext( "..\\WEB-INF\\applicationContext.xml"); SessionFactory sessionFactory = (SessionFactory) cxt .getBean("sessionFactory"); session = sessionFactory.openSession(); FullTextSession fullTextSession = Search .createFullTextSession(session); Query query = session.createQuery("from Book") .setMaxResults(1000); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); int j = 0; int sum = 0; log.info("start Indexing at "+df.format(Calendar.getInstance().getTimeInMillis())); while (true) { session.getTransaction().begin(); List<PvmsMaterialProgram> list = query.setFirstResult(j).list(); for (PvmsMaterialProgram program : list) { fullTextSession.index(program); } session.getTransaction().commit(); j += 1000; if (list.size() < 1000) break; sum += list.size(); log.info("finish " + sum + " records"); } log.info("end at "+df.format(Calendar.getInstance().getTimeInMillis())); } catch (Exception e) { e.printStackTrace(); } finally { if (session != null) session.close(); } } }
这里每一千条会提交一次。如果出现内存溢出,可以把虚拟机的内存调大一些。
索引构建好了,就可以开始使用查询功能了。Hibernate search支持复杂检索 - 支持Wild Card(诸如*, ?等通配符号),多关键字,模糊查询,排序等。可以使用封装好的接口也可以调用底层的lucene接口(参考官方的手册)。
session = sessionFactory.getCurrentSession(); tx = session.beginTransaction(); String keyword = "足球" FullTextSession fullTextSession = Search.createFullTextSession(session); //这里同时对title和摘要字段进行检索 String[] fields = { "title", "summarize" }; String[] values = { keyword, keyword}; MultiFieldQueryParser parser = new MultiFieldQueryParser( org.apache.lucene.util.Version.LUCENE_30, fields,new MMSegAnalyzer()); org.apache.lucene.search.Query luceneQuery = parser.parse( org.apache.lucene.util.Version.LUCENE_30, values, fields,new MMSegAnalyzer()); //按发布时间排倒序 org.apache.lucene.search.Sort sort = new Sort(new SortField( "publishDate", SortField.LONG, true)); FullTextQuery hibQuery = fullTextSession.createFullTextQuery(luceneQuery, Book.class); // hibQuery.setSort(sort); //分页设置 hibQuery.setFirstResult(start); hibQuery.setMaxResults(limit); tx.commit(); // log.info("query list = "+hibQuery.list()); List books = hibQuery.list();
session = sessionFactory.getCurrentSession(); tx = session.beginTransaction(); String keyword = "足球" FullTextSession fullTextSession = Search.createFullTextSession(session); //这里同时对title和摘要字段进行检索 String[] fields = { "title", "summarize" }; String[] values = { keyword, keyword}; MultiFieldQueryParser parser = new MultiFieldQueryParser( org.apache.lucene.util.Version.LUCENE_30, fields,new MMSegAnalyzer()); org.apache.lucene.search.Query luceneQuery = parser.parse( org.apache.lucene.util.Version.LUCENE_30, values, fields,new MMSegAnalyzer()); //按发布时间排倒序 org.apache.lucene.search.Sort sort = new Sort(new SortField( "publishDate", SortField.LONG, true)); FullTextQuery hibQuery = fullTextSession.createFullTextQuery(luceneQuery, Book.class); // hibQuery.setSort(sort); //分页设置 hibQuery.setFirstResult(start); hibQuery.setMaxResults(limit); tx.commit(); // log.info("query list = "+hibQuery.list()); List books = hibQuery.list();
这里对title和summarize字段检索关键词"足球",并采取分页查询的策略。
重建索引也非常简单
FullTextSession fullTextSession = Search .getFullTextSession(sessionFactory.getCurrentSession()); //异步 fullTextSession.createIndexer().start(); //同步 //fullTextSession.createIndexer().startAndwait();