hibernate search 我就不多说了,它是基于lucene的全文检索工具,记得上大学那时候接触了compass全文检索工具,后来也没怎么用,再后来这家伙不更新了,所以hibernate就推出了自己的基于lucene的全文检索工具就是这家伙hibernate Search。
不用说天然的优势就是可以无缝的和hibernate集成甚至不需要什么配置,一直在更新中,最近想在自己的博客里面加入搜索功能,本想用比较热乎的solr,找了半天资料还是放弃了,用在我的博客里面有点大题小做了,所以自然就锁定了hibernate search,简单嘛,而且不需要什么配置对hibernate jpa都很有好,虽然我用的是spring data jpa 但是我想也不会影响它的使用。
废话就不说了,我们进入正题,从配置开始:
1.persistence.xml 文件里面加入:
<property name="hibernate.search.default.directory_provider" value="filesystem"/>
<property name="hibernate.search.default.indexBase" value="e:/index"/>
如果是linux系统就把路径换一下,配置搞定,是不是很简单,其他地方不需要任何改动。
2.实体:
这不用解释了吧,你想要在那个实体上面做检索就配置那个实体,说白了就是加一些注解。
我就简略的写了,至于注解的含义大家在网上找找。
@Indexed
@Analyzer(impl=SmartChineseAnalyzer.class)
public class Posts implements java.io.Serializable {
@Id //这里不需要加什么,如果你用的是hibernate做持久层需要加@DocumentId 这个注解
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "post_title", nullable = false,length=1000)
@Field
@Boost(2)
public String getPtitle() {
return ptitle;
}
public void setPtitle(String ptitle) {
this.ptitle = ptitle;
}
@Lob
@Field
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
还需要我解释么,我觉得不需要了。
就这么简单,加几个注解就可以了,系统在运行时会自动生成索引文件
3.检索服务:
这个主要是放在service层,因为我用的是spring data jpa
@Service("postService")
public class PostServiceImpl implements PostService{
@Autowired
private EntityManagerFactory emf;
这里我在这个实现类里面注入了在applicationContext.xml 里面配置的EntityManagerFactory, 我想你应该懂这些
public QueryResult<Posts> search(int nowpage,int size,String keyWord) { QueryResult<Posts> queryResult = new QueryResult<Posts>(); EntityManager em = emf.createEntityManager(); FullTextEntityManager fManager = Search.getFullTextEntityManager(em); QueryBuilder qb = fManager.getSearchFactory().buildQueryBuilder().forEntity(Posts.class).get(); Query q = qb.keyword().onFields("ptitle","description","content").matching(keyWord).createQuery(); FullTextQuery fq = fManager.createFullTextQuery(q, Posts.class); queryResult.setTotalRecord(fq.getResultSize()); List<Posts> re = fq.setFirstResult(nowpage).setMaxResults(size).getResultList(); re = hightLight(q, re, Posts.class, null, "ptitle","description","content"); queryResult.setResultList(re); return queryResult; }
/** * @param org.apache.lucene.search.Query luceneQuery * @param searchResults 搜索结果集 * @param searchResultClass 搜索结果类型 * @param excludeFields 要排除高亮的字段 * @param fieldNames 需要高亮的字段 * @return 高亮后的searchResults */ private <E> List<E> hightLight(Query luceneQuery, List<E> searchResults, Class<E> searchResultClass, List<String> excludeFields, String... fieldNames) { SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<b><font color=\"red\">", "</font></b>"); QueryScorer queryScorer = new QueryScorer(luceneQuery); Highlighter highlighter = new Highlighter(formatter, queryScorer); Analyzer analyzer = new SmartChineseAnalyzer(); for (E e : searchResults) { for (String fieldName : fieldNames) { if(null != excludeFields && excludeFields.contains(fieldName)){ continue; } Object fieldValue = ReflectionUtils.invokeMethod(BeanUtils.getPropertyDescriptor(searchResultClass, fieldName).getReadMethod(), e); String hightLightFieldValue = null; if(fieldValue instanceof String){ try { hightLightFieldValue = highlighter.getBestFragment( analyzer, fieldName , String.valueOf(fieldValue)); } catch (Exception e1) { e1.printStackTrace(); } ReflectionUtils.invokeMethod(BeanUtils.getPropertyDescriptor(searchResultClass, fieldName).getWriteMethod(), e, hightLightFieldValue); } } } return searchResults; }
解释:onFields("ptitle","description","content") 这几个就是你要检索的字段,有几个写几个。
这里面加入了分页效果,高亮效果,这段代码我也是在网上找的修改了一下,如果谁还有更好的可以留言分享一下。
package com.weirblog.fenye; import java.util.List; /** * 查询结果集,包括数据和总数 * @author db2admin * * @param <T> */ public class QueryResult<T> { /** 查询得出的数据List **/ private List<T> resultList; /** 查询得出的总数 **/ private int totalRecord; public List<T> getResultList() { return resultList; } public void setResultList(List<T> resultList) { this.resultList = resultList; } public int getTotalRecord() { return totalRecord; } public void setTotalRecord(int totalRecord) { this.totalRecord = totalRecord; } }
package com.weirblog.fenye; import java.util.List; /** * 分页数据包装,包括分页信息和List数据 */ public class PageView<T> { /** 分页数据 **/ private List<T> records; /** 页码开始索引和结束索引 **/ private PageIndex pageIndex; /** 总页数 **/ private int totalPage = 1; /** 每页显示记录数 **/ private int maxResult = 10; /** 当前页 **/ private int currentPage = 1; /** 总记录数 **/ private int totalRecord; /** 每次显示多少页,必须保证大于3页,保证左右链接都可以使用 **/ private int viewPageCount = 10; /** 要获取记录的开始索引 **/ public int getFirstResult() { return (this.currentPage - 1); // return (this.currentPage - 1) * this.maxResult; } public int getViewPageCount() { return viewPageCount; } public void setViewPageCount(int viewPageCount) { this.viewPageCount = viewPageCount; } public PageView(int maxResult, int currentPage) { this.maxResult = maxResult; this.currentPage = (currentPage <= 0 ? 1 : currentPage); } public PageView(int currentPage) { this.currentPage = (currentPage <= 0 ? 1 : currentPage); } public void setQueryResult(QueryResult<T> qr) { setTotalRecord(qr.getTotalRecord()); setRecords(qr.getResultList()); } public int getTotalRecord() { return totalRecord; } public void setTotalRecord(int totalRecord) { this.totalRecord = totalRecord; setTotalPage(this.totalRecord % this.maxResult == 0 ? this.totalRecord / this.maxResult : this.totalRecord / this.maxResult + 1); } public List<T> getRecords() { return records; } public void setRecords(List<T> records) { this.records = records; } public PageIndex getPageIndex() { return pageIndex; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; this.pageIndex = PageIndex.getPageIndex(viewPageCount, currentPage, totalPage); } public int getMaxResult() { return maxResult; } public int getCurrentPage() { return currentPage; } }
这些关于分页的封装我就不解释了。
4.controller层:
@RequestMapping("/search") public String search(Integer page,String keyWord,Model model) { PageView<Posts> pageView = new PageView<Posts>(2, page!=null ? page:1); pageView.setQueryResult(postService.search(pageView.getFirstResult(), pageView.getMaxResult(), keyWord)); model.addAttribute("pageView", pageView); return "/search"; }
这个也应该不需要解释什么。
还有一些就是jar包了我用的是最新稳定版本5.3.0
hibernate-search-engine-5.3.0.Final
hibernate-search-orm-5.3.0.Final
lucene-analyzers-common-4.10.4
lucene-core-4.10.4
xml-apis-1.3.03
E:\gj\hibernate-search-5.3.0.Final\dist\lib\required
重复的去掉
hibernate 需要也是最新的了4.3.10
lucene需要:
lucene 这些有的hibernate search包里面没有 自己去lucene官网上下载,还有一个办法就是自己搭建一个maven工程加入:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>5.3.0.Final</version> </dependency>
就是这样才搞定了jar包缺失的问题,很扯淡是不是。
最后还是看看效果吧:
还行吧。