抽取BaseService
到目前为止,我们已经写了三个模块的开发了。我们已经抽取过了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。
抽取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();
}
}
以Info模块举例子
- InfoService
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参数的方法就行了。