提要
本文是 小小商城-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 查询器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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]; } }
@Autowired public 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 ,抽象增改删查方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| public class BaseServiceImpl<P> extends Service4DAOImpl<P> implements BaseService {
@Override public 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(); }
@Override public Integer add(Object object) { Session session = getSession(); return (Integer)session.save(object); }
@Override public void update(Object object) { Session session = getSession(); session.update(object); }
@Override public void delete(Object object) { Session session = getSession(); try { 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 即可,无需再写任何代码
1 2 3 4
| @Service public class ProductServiceImpl extends BaseServiceImpl implements ProductService {
}
|
在 Action 中调用
那么,我们应该怎么调用这些 Service 呢,其实非常简单,在注入了 productService 之后,只需直接调用 list 方法即可
1 2 3 4 5 6
| @Action("category") public String category(){ fill(category); products = productService.list("category",category,handleSort()[0],handleSort()[1],"stock_gt",0); return "categoryPage"; }
|
那么,大功告成,Service 和 Action 都省下了 大量重复的代码。