hibernate search5.3.0全文检索集成到spring data jpa

2015/08/10 11:08
阅读数 2.2K

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包缺失的问题,很扯淡是不是。


最后还是看看效果吧:

还行吧。

展开阅读全文
加载中
点击加入讨论🔥(5) 发布并加入讨论🔥
打赏
5 评论
6 收藏
0
分享
返回顶部
顶部