12cd.com 开发分享-- 示例代码 以及 如何简化JAVA WEB开发
12cd.com 是我08年十二月份由我负责开发的第一个比较正式的项目。
当时比较理想化 也比较雄心。网站是做原创音乐分享的,典型的web2.0,功能 实现 包括 流媒体,多种Flash播放器,实时搜 索,微博,用户空间装扮,论坛,新闻发布等
呵呵 题外话了,现在回顾一下,心得是:精心思考如何构建你的基础设施,可以极大的简化java web的开发。
通常使用SSH2开发,会有下面几部分组成:
view
这一层你可以使用多种视图技术,比如freemarker 或者jsp.在12cd中使用的jsp,不过 freemarker在项目中也有使用。
controller
这一层是直接和用户交互的一层。
service
对dao调用,封装一些业务操作。比如加好友就属于一个逻辑操作
dao
对hibernate第二次封装
假设我们是刚开始添加相册功能(ps:如果假设相册方面的功能之前已经有程序实现,现在只是简单添加自己查看相册列表的功能,那么下面的类应该已经存在,无需新建,你只要到相应的类上添加相应的方法即可)
首先 需要创建一个Controller,一个Serivce,我们分别叫他们AlbumsManager和AlbumsService(ps:项目中所有Controller 统一加Manager后缀,Service同理)。
先瞧瞧Service的代码:
package com.snail.example.album;
import java.util.Map; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import com.snail.base.impl.SBase_Service; import com.snail.commons.util.FunnyHashMap; import com.snail.component.beans.Xiangce; import com.snail.component.viewbeans.Page; @Service("s_albums_service") public class AlbumsService extends SBase_Service{ /* * 结果值包含下面几个key值 * list 一个包含结果POJO的List集合 * pager 一个分页对象 如现在是第几页 总共数据库有多少条记录等 * paginate paginate 分页结果 比如 << 上一页 1 2 3 ... 7 8 下一页 >> 使用freemarker模版,所以样式可以自定义 * new_paginate 现在12cd 统一使用的一个新的默认分页模板 */ public Map find_all(Page page) { //hql 形式查询 String hql="from Xiangce xangce where xiangce.huiyuan=:huiyuan order by xiangce.addTime desc"; Map result=find_results(page, null, hql, new FunnyHashMap("huiyuan",page.getHuiyuan())); //criteria 形式查询 result=find_results(page, Xiangce.class, new Order[]{Order.desc("addTime")}, new Criterion[]{Restrictions.eq("huiyuan", page.getHuiyuan())}); // detachedCretiria DetachedCriteria _dc=DetachedCriteria.forClass(Xiangce.class); _dc.add(Restrictions.eq("huiyuan", page.getHuiyuan())); _dc.addOrder(Order.desc("addTime")); result=find_results(page, _dc); //此外 DAO类还提供了 远程hibernate session 的调用接口 ,原生sql,hibernte 命名查询等接口 如不过上面的三种查询 //已经基本能够满足12cd.com大大部分查询 // s_base_dao.hibernate_session_execute(MethodObject.Function(new Object(){ // public List find_xiangces(Session session) // { // // } // }, null)); return result; } }
Ok ,就这么多,实际上上面的代码讲了三种形式的查询,真正只要使用其中的一种即可,也就是说一个Service的方法可以简化到三行代码。精简之后的话你应该是只要添加如下一个方法即可
public Map find_all(Page page) { String hql="from Xiangce as x where x.huiyuan=:huiyuan order by x.addTime desc"; return find_results(page, null, hql, new FunnyHashMap("huiyuan",page.getHuiyuan())); }
总共五行代码(注意service里面的注解使用,这样就不需要到spring配置文件中添加配置了)。
完成service后,我们还需要一个额外的步骤 在所有Controller的基类SBaseManager 注册我们新添加的Service类
public class SBaseManager extends ActionSupport implements Preparable @Resource(name="s_albums_service") protected AlbumsService s_albums_service;
也就添加了两行代码 不是很麻烦对吧
现在让我们看看 controller类的写法:
package com.snail.example.album; import com.snail.base.impl.SBaseManager; @Controller("albums_manager") public class AlbumsManager extends SBaseManager{ public String index() { //将结果放在request中 这样 后续的jsp页面可以使用 request("results",s_albums_service.find_all(page())); return default_view_dir("index"); } //这个方法不是强制的。不过方便找到渲染结果jsp页面 private String default_view_dir(String viewName) { view_url = "/com/snail/example/albums/"+viewName+".jsp"; return SUCCESS; } }
Controller 中 default_view_dir 方法是每个Controller都推荐提供的,主要是方便找到页面渲染,自动不全jsp页面路径。
这么看来Controller层的一个方法也就五行代码 。很少 对不对? 如果你接着要写查看某个相册所有照片的功能,连类也不用新建,直接在这个controller上添加一个show方法就可以了。哈哈
接着看看 我们的 View层,jsp页面 看下面:
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<link href="css/index.css" rel="stylesheet" type="text/css" />
<title>用户相册列表</title>
</head>
<body>
<div class="content">
<s:iterator value="#request.results['list']">
<p><s:property value="xiangCeMing"/></p>
</s:iterator>
</div>
<div class="page">
<s:property value="#request.results['new_paginate']" escape="false"/>
</div>
</body>
</html>
其中
<s:property value="#request.results['new_paginate']" escape="false"/>
这一句是输出分页标签的.很简单吧(个人不觉的比 <%=will_paginate @albums%> 麻烦多少。呵呵 开个玩笑)
额 还忘了一件事情,struts的的url配置我们是使用xml文件配置的,为什么不用注解呢? 额 因为当时我们这个项目是08年年底12月开始的,那个时候好像struts2才刚出来没多久。当时作开发也是一边看教程 一边看开发的(有点像以前javaeye用rails改写的时候人手一册 Web开发敏捷之道一样 哈哈)
看看配置文件里面的都要添加什么:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!--我们的配置文件继承自 snail_auth_base 也就是默认会有登录验证拦截器。如果你某个Action不需要登录验证,那么 在写Action标签的时候加上 <interceptor-ref name="normalStack"></interceptor-ref>即可 --> <package name="salbums" extends="snail_auth_base" namespace="/salbums"> <action name="index" method="index" class="albums_manager"> <result>${view_url}</result> </action> </package> </struts>
不多 也就三行代码(红色标识)。Ok ,所有流程走完了。看看我们的结果吧
额 现在访问一下 :
http://www.12cd.com/salbums/index.action
结果页面:
整个功能添加的流程相当简洁。分页,用户权限验证等都已经通过封装由系统自动完成。比如用户权限验证,在配置文件中 只要 package extends="snail_auth_base"就可以
实现登录拦截。如果用户没有的登录访问这个url,那么就会被导向到登录页面。如果不需要权限或者想自己在代码中判断只需要加一行红色的配置。
<action name="index" method="index" class="albums_manager"> <interceptor-ref name="normalStack"></interceptor-ref> <result>${view_url}</result> </action>
那么这一切是如何实现的?
我们看看12cd的四层结构(下面我只是讨论了三层)的基类是如何组织的。
Controller
Controller层 当然我把显示用的View层也算到了Controller层了。这一层对应的就是一个*Manager类和一张JSP页面。但是如何对Contoller进行划分是个难题,方法很多,比如根据功能,相同功能的比如登录,注册放在一个 Controller中。12cd采用的主体是根据Hibernate的POJO类,或者说是根据表来进行划分的(以资源来组织Contoller),辅助性的根据功能命名一些类。比如上面的相册,就是针对POJO Xiangce 设计的一个Controller.这有点像Rails里面Resources的处理方式,如果无法都用resrouce,还提供match 这种路由方式给你。
所有Contoller类都必须继承我提供的这个基类,基本代码如下
SBaseManager
public class SBaseManager extends ActionSupport implements Preparable { @Resource(name="s_huiyuan_service") protected SHuiyuan_Service s_huiyuan_service; @Resource(name="s_comments_service") protected SComments_Service s_comments_service; @Resource(name="s_albums_service") protected AlbumsService s_albums_service; --end //常用数据 不允许在子类中重复定义 protected String view_url=""; protected HttpServletRequest request = null; protected HttpServletResponse response = null; protected HttpSession session = null; protected ActionInvocation invocation = null; public Huiyuan huiyuan = null; protected String pageMethod; protected Integer currentPage; protected Integer pageSize; protected Integer totalRows; protected ServletContext sc = null; protected String request_url=null; protected String request_url_referer=null; protected String div_id=null; protected String render_type; protected ICacheClient cache_client=null; //通常进行一个操作有三个状态 view->进入操作页面 // process->处理页面 // protected String s_operate_type=null; //header 常量定义// protected static final String ENCODING_PREFIX = "encoding"; protected static final String NOCACHE_PREFIX = "no-cache"; protected static final String ETAG_PREFIX = "ETag"; protected static final String LASTMODIFIED_PREFIX = "Last-Modified"; protected static final String ENCODING_DEFAULT = "GBK"; protected static final boolean NOCACHE_DEFAULT = true; //content-type 定义 // protected static final String TEXT_TYPE = "text/plain"; protected static final String JSON_TYPE = "application/json"; protected static final String XML_TYPE = "text/xml"; protected static final String HTML_TYPE = "text/html"; //----------------------------------实用函数功能区-------------------------------------------- begin public boolean empty(Object _object) { return SUtils.empty(_object); } public boolean ajax_request() { String _temp=(String)request.getHeader("X-Requested-With"); return _temp.equalsIgnoreCase("XMLHttpRequest"); } protected HttpServletRequest request(String key, Object object) { request.setAttribute(key, object); return request; } public Page page() { Page page=new Page(currentPage, pageSize, pageMethod,"",construct_request_url(),construct_request_parameters(),huiyuan,request,session,sc,invocation); page.setDiv_id(div_id); return page; } protected Object request(String key) { return request.getAttribute(key); } protected Object parameter(String key) { return request.getParameter(key); } protected HttpSession session(String key, Object object) { session.setAttribute(key, object); return session; } protected Object session(String key) { return session.getAttribute(key); } protected void s_config_page_size(Integer default_num) { if (this.pageSize == null) { this.pageSize = default_num; } } //如果是ajax请求则解码,否则返回原内容 public String soft_decode(String _content,String..._char_set) { if(empty(_content))return ""; if(ajax_request()) return SUtils.decode(_content, _char_set); else return _content; } //强制解码 public String force_decode(String _content,String..._char_set) { if(empty(_content))return ""; return SUtils.decode(_content, _char_set); } /* * 如果 _render_type 不为空且是渲染字符串,那么将_result_code_or_str_content(如果存在) * 渲染到浏览器,并且返回null * 否则 返回_result_code_or_str_content(如果存在,否则返回success) * */ protected String render(String _render_type,String _result_code_or_str_content) { Map<String,ResultConfig> _results=invocation.getProxy().getConfig().getResults(); List _result_codes=new ArrayList(_results.keySet()); if(!empty(_render_type)&&_render_type.equals(Constant.render_string)) { if(empty(_result_code_or_str_content))return null; out(_result_code_or_str_content) ;return null; } return (empty(_result_code_or_str_content)||!_result_codes.contains(_result_code_or_str_content))?SUCCESS:_result_code_or_str_content; } protected void out(String s) { response.setContentType(TEXT_TYPE+";charset="+ENCODING_DEFAULT); PrintWriter out = null; try { out = response.getWriter(); out.print(s); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } /* * 基础配置选项 */ public void prepare() throws Exception { this.request = ServletActionContext.getRequest(); this.response = ServletActionContext.getResponse(); this.session = this.request.getSession(); this.invocation = ServletActionContext.getContext().getActionInvocation(); this.sc = ServletActionContext.getServletContext(); String _temp=construct_request_parameters(); this.request_url = construct_request_url() +(empty(_temp)?"":("?" + construct_request_parameters())); this.request_url_referer=request.getHeader("Referer"); cache_client=CacheManager.getCacheClient(CacheManager.memecached); huiyuan=(Huiyuan)session(Constant.SESSION_KEY); } protected String construct_request_parameters() { StringBuffer param = new StringBuffer(); Map<String, String[]> zzMap = request.getParameterMap(); if (zzMap != null) { for (String s : zzMap.keySet()) { if (s.equals("pageSize") || s.equals("currentPage") || s.endsWith("pageMethod")||s.endsWith("totalRows")) { } else { String[] value = zzMap.get(s); for (String val : value) { param.append("&" + s + "=" + val); } } } } return param.toString(); } protected String construct_request_url() { String url = ""; String path = request.getContextPath(); String actionName = invocation.getProxy().getActionName(); String nameSpace = invocation.getProxy().getNamespace(); if (StringUtils.isNotEmpty(nameSpace)&&nameSpace.length()>1) { url = url + path + nameSpace; } if (StringUtils.isNotEmpty(actionName)) { url = url + "/" + actionName + ".action"; } return url; } }
上面去掉了一些Set/Get 方法。代码其实很简单。无非就是做了一些简单的封装。最简单的比如,
request.之前如果你需要使用的话必须像这样:
request.setAttribute("key","value");
但是经过简化继承这个基类,你在子类中就可以这么写:
request("key","value")
掰下手指,节省了多少个字符?上面的示例代码中也有例子:
request("results",s_albums_service.find_all(page()));
其实通过基类继承,我们可以很简单的模拟PHP的函数编程,而不是通过静态类调用静态方法。
在SBaseManager中 很重要的一个是page 方法。
java
public Page page() { Page page=new Page(currentPage, pageSize, pageMethod,"",construct_request_url(),construct_request_parameters(),huiyuan,request,session,sc,invocation); page.setDiv_id(div_id); return page; }
该方法在Controller构造了一个Page对象传递给Service层,这也是为什么Service层可以实现自动分页。不过比较麻烦的是这就要求每个Service方法的签名都必须有一个Page。额 是否可以通过AOP解决呢?
Service
Service层,基本对应Contoller层,封装一些数据库存取和逻辑相关的东东。在12CD中基本都和Controller一一对应。对于功能有交集的一些Service,我们把公用的方法放到了一个公共的Service类中。
下面看看基类的设计:
package com.snail.base.impl;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import com.snail.base.inter.ISBaseDAO; import com.snail.base.util.PagingFactory; import com.snail.base.util.SUtils; import com.snail.cache.impl.CacheManager; import com.snail.cache.inter.ICacheClient; import com.snail.commons.constant.Constant; import com.snail.commons.page.action.PageBean; import com.snail.commons.page.action.PageUtil; import com.snail.commons.util.MethodObject; import com.snail.component.beans.Huiyuan; import com.snail.component.viewbeans.Page; public class SBase_Service { @Resource(name = "s_base_dao") protected ISBaseDAO s_base_dao; protected ICacheClient cache_client=CacheManager.getCacheClient(CacheManager.memecached); //假设集合只有一个元素。如果是则返回该元素,法则返回null protected Object uniqueResult(List lists) { return empty(lists)?null:lists.get(0); } //分页 --begin //如果不需要排序或者不需要条件 将_orders和_criterions置为空数组即可,不可为null; public Map find_results(Page _contex,Class _clzz,Order[] _orders,Criterion[] _criterions) { int _totalRows=0; if(!empty(_contex.getTotalRows())&&_contex.getTotalRows()>0) _totalRows=_contex.getTotalRows(); else{ _totalRows=s_base_dao.find_entities_count_with_criteria(_clzz, _orders, _criterions);} PageBean pager = PageUtil.getPager(_contex.getCurrentPage(), _contex.getPageMethod(), _contex.getPageSize() == null ? 10 : _contex.getPageSize(), _totalRows); Map<String, Object> map = new HashMap<String, Object>(); map.put("list", s_base_dao.find_entities_with_criteria(_clzz, _orders, _criterions, pager.getStartRow(), pager.getPageSize())); map.put("pager", pager); map.put("paginate", quick_page(_contex,pager)); map.put("new_paginate", quick_page_new(_contex, pager)); return map; } //hql 查询,如果 _hql_count为""或者为null,那么层序会自动猜测统计语句 public Map find_results(Page _contex,String _hql_count,String _hql_select,Map _params) { int _totalRows=0; if(!empty(_contex.getTotalRows())&&_contex.getTotalRows()>0) _totalRows=_contex.getTotalRows(); else{ String _temp=_hql_count; if(empty(_hql_count))_temp=SUtils.auto_create_hql_count(_hql_select); _totalRows=s_base_dao.find_entities_count_with_hql(_temp,_params);} PageBean pager = PageUtil.getPager(_contex.getCurrentPage(), _contex.getPageMethod(), _contex.getPageSize() == null ? 10 : _contex.getPageSize(), _totalRows); Map<String, Object> map = new HashMap<String, Object>(); map.put("list", s_base_dao.find_entities_with_hql(_hql_select, _params, pager.getStartRow(), pager.getPageSize())); map.put("pager", pager); map.put("paginate", quick_page(_contex,pager)); map.put("new_paginate", quick_page_new(_contex, pager)); return map; } //detachedCretiria查询 public Map find_results(Page _contex,DetachedCriteria _dc) { int _totalRows=0; if(!empty(_contex.getTotalRows())&&_contex.getTotalRows()>0) _totalRows=_contex.getTotalRows(); else{ _totalRows=s_base_dao.find_entities_count_with_detached_criteria(_dc);} PageBean pager = PageUtil.getPager(_contex.getCurrentPage(), _contex.getPageMethod(), _contex.getPageSize() == null ? 10 : _contex.getPageSize(), _totalRows); Map<String, Object> map = new HashMap<String, Object>(); map.put("list", s_base_dao.find_entities_with_detached_criteria(_dc, pager.getStartRow(), pager.getPageSize())); map.put("pager", pager); map.put("paginate", quick_page(_contex,pager)); map.put("new_paginate", quick_page_new(_contex, pager)); return map; } // 输出分页内容(无模板) 格式为 首页 上一页 (下拉框) 下一页 最后一页 public String quick_page(Page _context,PageBean _pager, List... _config) { List list = _config.length == 0 ? new ArrayList() : _config[0]; list.add(0, _context.getRequest_params()); return PagingFactory.paging(_context.getRequest_url(), _pager, list); } //输出分页内容(有模板) 格式为 首页 上一页 2 3 4 下一页 最后一页 统计 public String quick_page_new(Page _context,PageBean _pager, List... _config) { List list = _config.length == 0 ? new ArrayList() : _config[0]; list.add(0, _context.getRequest_params()); return PagingFactory.paging_new(_context.getRequest_url(), _pager, list); } // ajax 版本 输出分页内容(无模板) 格式为 首页 上一页 (下拉框) 下一页 最后一页 public String quick_ajax_page(Page _context,PageBean _pager, String _div_id,List... _config) { List list = _config.length == 0 ? new ArrayList() : _config[0]; list.add(0, _context.getRequest_params()); return PagingFactory.paging_ajax(_div_id, _context.getRequest_url(), _pager, list); } //ajax 版本 输出分页内容(有模板) 格式为 首页 上一页 2 3 4 下一页 最后一页 统计 public String quick_ajax_page_new(Page _context,PageBean _pager, String _div_id,List... _config) { List list = _config.length == 0 ? new ArrayList() : _config[0]; list.add(0, _context.getRequest_params()); return PagingFactory.paging_ajax_new(_div_id, _context.getRequest_url(), _pager, list); } public Map config_ajax_page(Page _context,Map map,String div_id,boolean isNewAjax,List...config) { if(empty(div_id)){ map.put("new_paginate",quick_page_new(_context,(PageBean)map.get("pager"),config)); }else{ if(isNewAjax) { map.put("new_paginate", quick_ajax_page_new(_context,(PageBean)map.get("pager"),div_id,config)); } else { map.put("paginate", quick_ajax_page(_context,(PageBean)map.get("pager"),div_id,config)); } } return map; } // --end //该方法返回的Page对象适合不分页的存取,或者只是为了做占位符 public Page page(int num) { Page page=new Page(1, num, "","","","",null,null,null,null,null); return page; } public boolean empty(Object _object) { return SUtils.empty(_object); } }
这个基类重点做了下面几件事情:
- 实现自动分页
- 提供了多个封装好的查询方法
DAO
DAO层。DAO层在12CD属于很薄的一层。只有一个接口和一个实现类。我觉得针对每个POJO设计一个DAO类是很愚蠢 的方式,或者说是一种过度设计吧。12cd项目中使用了一个泛型DAO.提供的功能基本够用
下面是12CD的DAO接口
public interface ISBaseDAO<T> { //基本增删改查 --begin public T find_entity(Class _clzz,Serializable _id); public void update_entity(T _entity); public void save_entity(T _entity); public void merge_entity(T _entity); public void save_update_entity(T _entity); public void save_update_entities(Collection<T> _entities); public void delete_entity(T _entity); //dateType,如果是删除某一段日期的记录,必须指明是类型,timeStamp或者dateTime //可以使用Constant中的日期,例如Contant.time_stamp 和Contant.date_time,默认为time_stamp public void delete_entities(String _hql,Map _params,String..._dateType); public void delete_entities(Collection _entities); // --end //查询 ps :推荐使用detached_crieria或者hql查询 --begin //使用criteria查询 public List find_entities_with_criteria(Class clzz,Order[] _orders,Criterion[] _criterions,int _start,int _num); public List find_all_entities_with_criteria(Class clzz,Order[] _orders,Criterion[] _criterions); public int find_entities_count_with_criteria(Class clzz,Order[] _orders,Criterion[] _criterions); //兼容之前的代码,使用不定参数 public List find_entities_with_criteria(Class clzz,int _start,int _num,Order[] _orders,Criterion... criterions); //使用detached_criteria查询 public List find_entities_with_detached_criteria(DetachedCriteria _dc,int _start,int _num); public List find_all_entities_with_detached_criteria(DetachedCriteria _dc); public int find_entities_count_with_detached_criteria(DetachedCriteria _dc); //使用hql查询 public List find_entities_with_hql(String _hql,Map _params,int _start,int _num); public List find_all_entities_with_hql(String _hql,Map _params); public int find_entities_count_with_hql(String _hql,Map _params); //集合过滤查询,比如需要查出歌曲点击数大于30的歌曲, //可以用find_entities_with_filter(huiyuan.gequs,"this.dianJiShu>30") public List find_entities_with_filter(Collection _collections,String _filter,int _start,int _num); //hibernate 命名查询 public List find_entities_with_named_query(String _query,Map _params,int _start,int _num); // --end //批量更新 public int update_entities_with_hql(String _query,Map _params); //统计或者有用的方法 --begin //由于hibernate必须初始化集合(即将集合中元素从数据库中的 //取出)才能统计集合元素数目。所以额外添加该方法用于提高效率。 //使用的时候注意 public int count_collection_size(Collection _collection); //获得命名查询的待执行hql语句 public String hql(String _name_query_string_name); // --end //较为底层的方法 --begin //通过mo回调获得session执行相应操作 public Object hibernate_session_execute(MethodObject _mo); //执行本地sql语句 public List find_entities_with_native_sql(String hql,Map params,int start,int num); public List find_entities_with_native_sql(String hql,Map params); // --end }
个人觉得这些方法大部分情况下已经够用了。
下面的内容是关于搜索方面的。12CD因为使用的的是Hibernate,搜索自然使用了以Lucene为基础的Compass.
Compass和Hibenate可以很好的结合。目前我是将Compass的注解直接写在Hibernate的Pojo文件上的,Hibernate映射则使用xml配置文件。
只要将Compass集成到Spring配置 就可以实现自动实时索引。基本不用担心太多。12cd提供了一个查询类,大家可以参考一下。(因为代码比较长,无关的代码已经被删除)
package com.snail.commons.service; import static com.snail.commons.util.MethodObject.Function; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.compass.core.Compass; import org.compass.core.CompassCallback; import org.compass.core.CompassException; import org.compass.core.CompassHit; import org.compass.core.CompassHits; import org.compass.core.CompassQuery; import org.compass.core.CompassQueryBuilder; import org.compass.core.CompassSession; import org.compass.core.CompassTemplate; import org.compass.core.CompassQuery.SortPropertyType; import org.compass.core.CompassQueryBuilder.CompassBooleanQueryBuilder; import org.compass.core.support.search.CompassSearchCommand; import org.compass.core.support.search.CompassSearchResults; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.springframework.stereotype.Service; import com.snail.commons.page.action.PageBean; import com.snail.commons.util.DFUtil; import com.snail.commons.util.FunnyHashMap; import com.snail.commons.util.FunnyList; import com.snail.commons.util.MethodObject; import com.snail.component.beans.Searchstatistics; import com.snail.component.viewbeans.Page; import com.snail.search.action.AdvancedCompassSearchHelper; @Service("search_service") public class SearchService extends Base_Service { private final static Logger logger = Logger.getLogger(SearchService.class); public Map find(final Page page, final String keywords, final String[] clzz_names, final Map<String, String> highlightFields, final List<CompassQuery> queries) { return base_find(page, keywords, clzz_names, highlightFields, Function(new Object() { public CompassQuery construct_queries(CompassQueryBuilder cqb) { CompassBooleanQueryBuilder cbqb = cqb.bool(); for (CompassQuery cq : queries) { cbqb.addMust(cq); } cbqb.addMust(cqb.queryString(keywords).toQuery()); return empty(clzz_names) ? cbqb.toQuery() : cbqb.toQuery().setAliases(clzz_names); } })); } public Map base_find(final Page page, final String keywords, final String[] clzz_names, final Map<String, String> highlightFields, final MethodObject mo) { return process_index(new CompassCallback() { public Object doInCompass(CompassSession session) throws CompassException { Map result = new HashMap(); List list = new ArrayList(); AdvancedCompassSearchHelper search = new AdvancedCompassSearchHelper(compass, page.getPageSize()); if (!empty(highlightFields)) { search.setHighlightFields(highlightFields); } CompassQueryBuilder cq = session.queryBuilder(); CompassQuery cqb = (CompassQuery) mo.invoke(cq); int totalRows = search.search(new CompassSearchCommand(cqb, 0)).getTotalHits(); PageBean pageBean = convert_page_to_pagebean(page, totalRows); CompassSearchCommand csc = new CompassSearchCommand(cqb, pageBean.getCurrentPage() - 1); CompassSearchResults results = search.search(csc); CompassHit[] hits = results.getHits(); for (int i = 0; i < hits.length; i++) { CompassHit hit = hits[i]; list.add(new FunnyHashMap("data", hit.getData(), "hit", hit)); } // logger.info("结果总数:" + results.getTotalHits() + "==页数" + results.getPages().length + "当前页面=>" + pageBean.getCurrentPage() + "/" // + ((nil(csc.getPage())) ? "nil" : csc.getPage().intValue())); logger.info("结果总数:" + results.getTotalHits()); result.put("list", list); result.put("hits", hits); result.put("search_time", results.getSearchTime()); result.put("paginate", empty(page.getAjax()) ? quick_page(page, convert_page_to_pagebean(page, results.getTotalHits())) : quick_ajax_page(page, convert_page_to_pagebean(page, results.getTotalHits()), page.getDiv_id())); result.put("new_paginate", quick_page_new(page, convert_page_to_pagebean(page, results.getTotalHits()))); result.put("pager", pageBean); return result; } }); } @Resource(name = "compass") Compass compass; @Resource(name = "compassTemplate") CompassTemplate compassTemplate; }
示例使用:
public String music_search()
{ String[] str ={ "Gequ" }; List k_list = new ArrayList(); config_page_size(24); Compass compass = search_service.getCompass(); CompassQuery query = compass.queryBuilder().term("Gequ.gequKind",geQuKind); Map<String, String> hilights = new FunnyHashMap("Gequ","geQuMing"); k_list.add(query); query = compass.queryBuilder().term("Gequ.shengHeZhuangTai",1); k_list.add(query); Map map = search_service.find(page(), keyword, str, hilights, k_list); request("search_map", map); return result(SUCCESS); }