tags: [小小商城, SSH]
categories: [技术实战]
提要
本文是 小小商城-SSH版的 细节详解系列 之一,项目 github:https://github.com/xenv/S-mall-ssh 本文代码大部分在 github 中 可以找到
在 SSH 开发中,Service层会有许多重复的,调用dao层的,增改删查的方法。对于重复的方法,我们可以用一个 BaseService 抽象出来。如图所示
普通Service的增改删查操作统一被抽象到 BaseService,普通Service继承BaseService后,只需实现少量独有的代码即可
这样,在 Action 层调用的时候,只需直接 categoryService.get(id) 就可以完成获取操作。
具体实现
BaseService实现的难点在于,不知道 继承它的 普通 Service 对应的 实体类 是哪个,从而不能从 Hibernate 中 获取到相应的 数据库查询器,那么,怎么来解决这个问题呢?
其实很简单,利用 泛型 ,子类在继承父类的时候实现泛型,初始化时,父类就可以读取到子类的泛型信息,从而实现了 子类 向 父类 传 数据。
父类根据子类携带的实体类信息,初始化 Hibernate 数据库查询器,子类在泛型继承的时候,就自动会调用子类指定的查询器,从而做到了抽象的效果。
在 小小商城 项目中,核心实现是这两个文件,Service4DAOImpl.java 和 BaseServiceImpl.java 下面,我会一步步教大家实现。
初始化时读取子类的泛型信息,创建 Hibernate 查询器
public class Service4DAOImpl<P> implements Service4DAO {protected DAO dao;protected Class clazz;public Service4DAOImpl() {ParameterizedType t = (ParameterizedType) (getClass().getGenericSuperclass());if (t != null) {clazz = (Class) t.getActualTypeArguments()[0];}}@Autowiredpublic void setDao(DAO dao) {this.dao = dao;}public Criteria createCriteria() {return getSession().createCriteria(clazz).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);}public Session getSession() {return dao.getSessionFactory().getCurrentSession();}}
通过反射,子类继承该类时,会自动读取到 子类 携带的 实体类 ,然后我们将 hibernate 的 dao 自动注入进来,使用 createCriteria 就可以获取到相应的查询器
比如 ProductService 中 extends Service4DAOImpl ,我们就可以自动为其创建一个 Product 实体类的查询器了。
实现 BaseService ,抽象增改删查方法
public class BaseServiceImpl<P> extends Service4DAOImpl<P> implements BaseService {@Overridepublic List list(Object... paramAndObjects) {Criteria c= createCriteria();if(paramAndObjects.length%2 !=0){return null;}for (int i=0;i<paramAndObjects.length;i+=2){if(paramAndObjects[i+1] == null){c.add(Restrictions.isNull(paramAndObjects[i].toString()));continue;}if(paramAndObjects[i].equals("pagination") && paramAndObjects[i+1] instanceof Pagination) {c.setFirstResult(((Pagination)paramAndObjects[i+1]).getStart());c.setMaxResults(((Pagination)paramAndObjects[i+1]).getCount());continue;}if(paramAndObjects[i].equals("desc") && paramAndObjects[i+1] instanceof String){c.addOrder(Order.desc(paramAndObjects[i+1].toString()));continue;}if(paramAndObjects[i].equals("asc") && paramAndObjects[i+1] instanceof String){c.addOrder(Order.asc(paramAndObjects[i+1].toString()));continue;}if(paramAndObjects[i].equals("max") && NumberUtils.isDigits(paramAndObjects[i+1].toString())){c.setMaxResults(Integer.valueOf(paramAndObjects[i+1].toString()));continue;}if(paramAndObjects[i].toString().contains("_like") && paramAndObjects[i+1] instanceof String){String keyword = "%"+paramAndObjects[i+1].toString()+"%";c.add(Restrictions.like(StringUtils.removeEnd(paramAndObjects[i].toString(),"_like"),keyword));continue;}if(paramAndObjects[i].toString().contains("_gt") && NumberUtils.isDigits(paramAndObjects[i+1].toString())){c.add(Restrictions.gt(StringUtils.removeEnd(paramAndObjects[i].toString(),"_gt"),paramAndObjects[i+1]));continue;}c.add(Restrictions.eq(paramAndObjects[i].toString(),paramAndObjects[i+1]));}c.addOrder(Order.desc("id"));return c.list();}@Overridepublic Integer add(Object object) {Session session = getSession();return (Integer)session.save(object);}@Overridepublic void update(Object object) {Session session = getSession();session.update(object);}@Overridepublic void delete(Object object) {Session session = getSession();try {//获取对象的setDeleteAt方法,插入一个时间Method setDeleteAt = object.getClass().getMethod("setDeleteAt",Date.class);setDeleteAt.invoke(object,new Date());} catch (Exception e) {e.printStackTrace();}session.update(object);}}
这里节选了项目里的文件的一部分代码,我们先看 update(Object object) ,在获取到 seesion 之后,我们直接调用 update 方法即可
在list方法中,我又对查询器进行进一步抽象,这样,我们在调用的时候,就可以顺带指定分页、顺序、搜索条件等,更加方便
普通Service 附带 上 泛型 信息
这样,我们直接继承 BaseService 即可,无需再写任何代码
@Service
public class ProductServiceImpl extends BaseServiceImpl implements ProductService {
}
在 Action 中调用
那么,我们应该怎么调用这些 Service 呢,其实非常简单,在注入了 productService 之后,只需直接调用list方法即可
@Action("category")
public String category(){
fill(category);
products = productService.list("category",category,handleSort()[0],handleSort()[1],"stock_gt",0);
return "categoryPage";
}
那么,大功告成,Service 和 Action 都省下了 大量重复的代码。
