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);
}