提要

本文是 小小商城-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.javaBaseServiceImpl.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 {
//获取对象的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 即可,无需再写任何代码

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 都省下了 大量重复的代码。