tags: [小小商城, SSH]
categories: [技术实战]


提要

本文是 小小商城-SSH版的 细节详解系列 之一,项目 github:https://github.com/xenv/S-mall-ssh 本文代码大部分在 github 中 可以找到

在 SSH 开发中,Service层会有许多重复的,调用dao层的,增改删查的方法。对于重复的方法,我们可以用一个 BaseService 抽象出来。如图所示

SSH开发 - 配合Hibernate,通过泛型实现 BaseService ,抽象增改删查方法 - 图1 普通Service的增改删查操作统一被抽象到 BaseService,普通Service继承BaseService后,只需实现少量独有的代码即可

这样,在 Action 层调用的时候,只需直接 categoryService.get(id) 就可以完成获取操作。

具体实现

BaseService实现的难点在于,不知道 继承它的 普通 Service 对应的 实体类 是哪个,从而不能从 Hibernate 中 获取到相应的 数据库查询器,那么,怎么来解决这个问题呢?

其实很简单,利用 泛型 ,子类在继承父类的时候实现泛型,初始化时,父类就可以读取到子类的泛型信息,从而实现了 子类 向 父类 传 数据。

父类根据子类携带的实体类信息,初始化 Hibernate 数据库查询器,子类在泛型继承的时候,就自动会调用子类指定的查询器,从而做到了抽象的效果。

在 小小商城 项目中,核心实现是这两个文件,Service4DAOImpl.javaBaseServiceImpl.java 下面,我会一步步教大家实现。

初始化时读取子类的泛型信息,创建 Hibernate 查询器

  1. public class Service4DAOImpl<P> implements Service4DAO {
  2. protected DAO dao;
  3. protected Class clazz;
  4. public Service4DAOImpl() {
  5. ParameterizedType t = (ParameterizedType) (getClass().getGenericSuperclass());
  6. if (t != null) {
  7. clazz = (Class) t.getActualTypeArguments()[0];
  8. }
  9. }
  10. @Autowired
  11. public void setDao(DAO dao) {
  12. this.dao = dao;
  13. }
  14. public Criteria createCriteria() {
  15. return getSession().createCriteria(clazz).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
  16. }
  17. public Session getSession() {
  18. return dao.getSessionFactory().getCurrentSession();
  19. }
  20. }

通过反射,子类继承该类时,会自动读取到 子类 携带的 实体类 ,然后我们将 hibernate 的 dao 自动注入进来,使用 createCriteria 就可以获取到相应的查询器

比如 ProductService 中 extends Service4DAOImpl ,我们就可以自动为其创建一个 Product 实体类的查询器了。

实现 BaseService ,抽象增改删查方法

  1. public class BaseServiceImpl<P> extends Service4DAOImpl<P> implements BaseService {
  2. @Override
  3. public List list(Object... paramAndObjects) {
  4. Criteria c= createCriteria();
  5. if(paramAndObjects.length%2 !=0){
  6. return null;
  7. }
  8. for (int i=0;i<paramAndObjects.length;i+=2){
  9. if(paramAndObjects[i+1] == null){
  10. c.add(Restrictions.isNull(paramAndObjects[i].toString()));
  11. continue;
  12. }
  13. if(paramAndObjects[i].equals("pagination") && paramAndObjects[i+1] instanceof Pagination) {
  14. c.setFirstResult(((Pagination)paramAndObjects[i+1]).getStart());
  15. c.setMaxResults(((Pagination)paramAndObjects[i+1]).getCount());
  16. continue;
  17. }
  18. if(paramAndObjects[i].equals("desc") && paramAndObjects[i+1] instanceof String){
  19. c.addOrder(Order.desc(paramAndObjects[i+1].toString()));
  20. continue;
  21. }
  22. if(paramAndObjects[i].equals("asc") && paramAndObjects[i+1] instanceof String){
  23. c.addOrder(Order.asc(paramAndObjects[i+1].toString()));
  24. continue;
  25. }
  26. if(paramAndObjects[i].equals("max") && NumberUtils.isDigits(paramAndObjects[i+1].toString())){
  27. c.setMaxResults(Integer.valueOf(paramAndObjects[i+1].toString()));
  28. continue;
  29. }
  30. if(paramAndObjects[i].toString().contains("_like") && paramAndObjects[i+1] instanceof String){
  31. String keyword = "%"+paramAndObjects[i+1].toString()+"%";
  32. c.add(Restrictions.like(StringUtils.removeEnd(paramAndObjects[i].toString(),"_like"),keyword));
  33. continue;
  34. }
  35. if(paramAndObjects[i].toString().contains("_gt") && NumberUtils.isDigits(paramAndObjects[i+1].toString())){
  36. c.add(Restrictions.gt(StringUtils.removeEnd(paramAndObjects[i].toString(),"_gt"),paramAndObjects[i+1]));
  37. continue;
  38. }
  39. c.add(Restrictions.eq(paramAndObjects[i].toString(),paramAndObjects[i+1]));
  40. }
  41. c.addOrder(Order.desc("id"));
  42. return c.list();
  43. }
  44. @Override
  45. public Integer add(Object object) {
  46. Session session = getSession();
  47. return (Integer)session.save(object);
  48. }
  49. @Override
  50. public void update(Object object) {
  51. Session session = getSession();
  52. session.update(object);
  53. }
  54. @Override
  55. public void delete(Object object) {
  56. Session session = getSession();
  57. try {
  58. //获取对象的setDeleteAt方法,插入一个时间
  59. Method setDeleteAt = object.getClass().getMethod("setDeleteAt",Date.class);
  60. setDeleteAt.invoke(object,new Date());
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. }
  64. session.update(object);
  65. }
  66. }

这里节选了项目里的文件的一部分代码,我们先看 update(Object object) ,在获取到 seesion 之后,我们直接调用 update 方法即可

在list方法中,我又对查询器进行进一步抽象,这样,我们在调用的时候,就可以顺带指定分页、顺序、搜索条件等,更加方便

普通Service 附带 上 泛型 信息

  1. 这样,我们直接继承 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 都省下了 大量重复的代码。