到目前为止,我们已经写了三个模块的开发了。我们已经抽取过了BaseAction、BaseDao,我们这次来看看我们的Service接口。
UserService /** * created by ozc on 2017/5/23. */ public interface UserService { //新增 void save(User user); //更新 void update(User user); //根据id删除 void delete(Serializable id); //根据id查找 User findObjectById(Serializable id); //查找列表 List<User> findObjects() throws ServiceException; //导出用户列表 void exportExcel(List<User> userList, ServletOutputStream outputStream); //导入用户列表 void importExcel(File userExcel, String userExcelFileName); /** * 根据帐号和用户id查询用户 * * @param id 用户ID * @param account 用户帐号 * @return 用户列表 */ List<User> findAccount(String id, String account); void saveUserAndRole(User user, String[] userRoleIds); //通过用户id得到该用户的角色 List<UserRole> findRoleById(String id); void deleteUserRoleById(String[] userRoleIds); List<User> findUserByAccountAndPassword(String account, String password); } InfoService /** * created by ozc on 2017/5/23. */ public interface InfoService { //新增 public void save(Info info); //更新 public void update(Info info); //根据id删除 public void delete(Serializable id); //根据id查找 public Info findObjectById(Serializable id); //查找列表 public List<Info> findObjects() ; } RoleService /** * Created by ozc on 2017/5/26. */ public interface RoleService { //新增 void save(Role role); //更新 void update(Role role); //根据id删除O void delete(Serializable id); //根据id查找 Role findObjectById(Serializable id); //查找列表 List<Role> findObjects() ; }我们可以发现,三个Service接口中都存在着增删改查的方法,这明显就是重复的代码。因此,我们需要将他们进行抽取成一个BaseService。
在core模块中添加service包,抽取BaseService
package zhongfucheng.core.service; import java.io.Serializable; import java.util.List; /** * Created by ozc on 2017/6/7. */ interface BaseService<T> { //新增 void save(T entity); //更新 void update(T entity); //根据id删除 void delete(Serializable id); //根据id查找 T findObjectById(Serializable id); //查找列表 List<T> findObjects(); } 抽取BaseServiceImpl我们的Sercive是调用dao层的对象来实现方法的,因为这个Service是代表整个项目的Service,于是应该使用BaseDao
package zhongfucheng.core.service.impl; import zhongfucheng.core.dao.BaseDao; import zhongfucheng.core.service.BaseService; import java.io.Serializable; import java.util.List; /** * Created by ozc on 2017/6/7. */ public abstract class BaseServiceImpl <T> implements BaseService <T>{ //通过BaseDao来操作数据库 private BaseDao<T> baseDao; @Override public void save(T entity) { baseDao.save(entity); } @Override public void update(T entity) { baseDao.update(entity); } @Override public void delete(Serializable id) { baseDao.delete(id); } @Override public T findObjectById(Serializable id) { return baseDao.findObjectById(id); } @Override public List<T> findObjects() { return baseDao.findObjects(); } }InfoService继承了BaseService接口,于是就有了增删改查的方法。同时把泛型T的类型确定下来。
/** * created by ozc on 2017/5/23. */ public interface InfoService extends BaseService<Info> { } InfoServiceImpl继承了InfoService,有了增删该查的方法,然而具体的操作是BaseServiceImpl中实现的。我们继承它,并给出泛型T对应的类型。
@Service public class InfoServiceImpl extends BaseServiceImpl<Info> implements InfoService { }现在的问题是什么呢???我们在BaseServiceImpl中使用了BaseDao这个变量来对数据库进行操作。可是在BaseServiceImpl中是没有BaseDao这个变量的。
首先,要明确的是,我们不能在BaseServiceImpl中注入BaseDao,因为BaseServiceImpl本身就是一个抽象类。那我们怎么对BaseDao进行实例化呢???
我们可以这样做:
在InfoServiceImpl本身就需要注入InfoDao,来对数据库的操作。而我们这一次不使用属性输入,使用set方法注入在注入的同时,在BaseServiceImpl中给BaseDao设置set方法。那么我们在注入的时候,就可以调用BaseDao的set方法,把我们要注入的对象给过去。最后,我们在BaseServiceImpl中就有了baseDao这个变量了。
InfoServiceImpl得到InfoDao对象,并把InfoDao对象设置到BaseServiceImpl中。
@Service public class InfoServiceImpl extends BaseServiceImpl<Info> implements InfoService { private InfoDao infoDao; @Resource public void setInfoDao(InfoDao infoDao) { super.setBaseDao(infoDao); this.infoDao = infoDao; } } BaseServiceImpl为BaseDao设置set方法。 //通过BaseDao来操作数据库 private BaseDao<T> baseDao; public void setBaseDao(BaseDao<T> baseDao) { this.baseDao = baseDao; }我们来实现下面的功能:
其实也是一个查询,只不过查询多了一个条件罢了。按照传统的方式我们可以这样做:
在BaseDao中声明一个方法,接收的是SQL和参数列表 //根据条件查询列表 List<T> findObjects(String sql, List<Object> objectList); 在BaseDaoImpl实现它 @Override public List<T> findObjects(String sql, List<Object> objectList) { Query query = getSession().createQuery(sql); if (objectList != null) { int i =0; for (Object o : objectList) { query.setParameter(i, o); i++; } return query.list(); } return query.list(); } BaseService定义它: //根据条件查询列表 List<T> findObjects(String sql, List<Object> objectList); BaseServiceImpl中使用baseDao来调用它 @Override public List<T> findObjects(String sql, List<Object> objectList) { return baseDao.findObjects(sql, objectList); } Action中处理请求我们还是用着listUI这个方法,因为它仅仅是参数可能不同。
如果用户使用的是条件查询,那么它应该有Info对象带过来。如果不是条件查询,就没有Info对象根据Info对象设置是否要设置参数来查询【在HQL语句中添加新字段】。所以这个方法通用。 public String listUI() { //查询语句 String hql = "FROM Info i "; List<Object> objectList = new ArrayList<>(); //根据info是否为null来判断是否是条件查询。如果info为空,那么是查询所有。 if (info != null) { if (StringUtils.isNotBlank(info.getTitle())) { hql += "where i.title like ?"; objectList.add("%" + info.getTitle() + "%"); } } infoList = infoServiceImpl.findObjects(hql,objectList); ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP); return "listUI"; }看回我们Action中的代码,我们可以看出一些不够优雅的地方:
于是,我们想要用一个工具类来把上面的代码进行优化。
针对上面的问题,我们发现手写拼接SQL很容易出错。那我们可以在工具类里面拼接,使用的时候调用方法获取就行啦。查询的对象写死了,我们要可以处理任何的查询。。
我们能够找到如下的规律:
FROM Info WHERE title like ? and state = ? order by createTime,state 条件查询(QueryHelper): 1、查询条件语句hql: from 子句:必定出现;而且只出现一次 where 子句:可选;但关键字where 出现一次;可添加多个查询条件 order by子句:可选;但关键字order by 出现一次;可添加多个排序属性 2、查询条件值集合: 出现时机:在添加查询条件的时候,?对应的查询条件值 工具类代码: package zhongfucheng.core.utils; import java.util.ArrayList; import java.util.List; /** * Created by ozc on 2017/6/7. */ public class QueryHelper { private String fromClause = ""; private String whereClause = ""; private String orderbyClause = ""; private List<Object> objectList; public static String ORDER_BY_ASC = "asc"; public static String ORDER_BY_DESC = "desc"; //FROM子句只出现一次 /** * 构建FROM字句,并设置查询哪张表 * @param aClass 用户想要操作的类型 * @param alias 别名 */ public QueryHelper(Class aClass, String alias) { fromClause = " FROM " + aClass.getSimpleName() + " " + alias; } //WHERE字句可以添加多个条件,但WHERE关键字只出现一次 /** * 构建WHERE字句 * @param condition * @param objects * @return */ public QueryHelper addCondition(String condition, Object... objects) { //如果已经有字符了,那么就说明已经有WHERE关键字了 if (whereClause.length() > 0) { whereClause += " AND " + condition; } else { whereClause += " WHERE" + condition; } //在添加查询条件的时候,?对应的查询条件值 if (objects == null) { objectList = new ArrayList<>(); } for (Object object : objects) { objectList.add(object); } return this; } /** * * @param property 要排序的属性 * @param order 是升序还是降序 * @return */ public QueryHelper orderBy(String property, String order) { //如果已经有字符了,那么就说明已经有ORDER关键字了 if (orderbyClause.length() > 0) { orderbyClause += " , " + property +" " + order; } else { orderbyClause += " ORDER BY " + property+" " + order; } return this; } /** * 返回HQL语句 */ public String returnHQL() { return fromClause + whereClause + orderbyClause; } /** * 得到参数列表 * @return */ public List<Object> getObjectList() { return objectList; } } Action处理: public String listUI() { QueryHelper queryHelper = new QueryHelper(Info.class, "i"); //根据info是否为null来判断是否是条件查询。如果info为空,那么是查询所有。 if (info != null) { if (StringUtils.isNotBlank(info.getTitle())) { queryHelper.addCondition(" i.title like ? ", "%" + info.getTitle() + "%"); } } queryHelper.orderBy("i.createTime", QueryHelper.ORDER_BY_DESC); infoList = infoServiceImpl.findObjects(queryHelper); //infoList = infoServiceImpl.findObjects(hql,objectList); ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP); return "listUI"; } 最后在dao、service层中添加一个queryHelper参数的方法就行了。