纳税服务系统十一【抽取BaseService、条件查询】

414 阅读7分钟

tags: 纳税服务系统项目


抽取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参数的方法就行了。

总结

  • 由于Service的代码重复性太高了,我们也将Service进行抽取。抽取成一个BaseService接口
  • BaseServiceImpl实现BaseService接口,但他要使用BaseDao对象来对实现的方法进行调用
    • 此时,BaseServiceImpl是一个抽象类,它本身不能实例化了。那怎么将BaseDao实例化呢??
    • 当我们的InfoServiceImpl继承继承着BaseServiceImpl时,本身就需要用到InfoDao来对该模块的业务进行调用。
    • 在InfoServiceImpl对InfoDao实例话是很容易的,可以使用自动装配,set方法注入等等。这次我们使用set方法注入!
    • set方法有什么好处??能够在InfoServiceImpl注入InfoDao对象的同时,还能进行其他操作。比如:调用父类的set方法!!!!!!!
    • 我们只要在BaseServiceImpl提供一个setBaseDao方法,子类再把自身的Dao传递进去。那么我们的BaseDao就被实例化了!
  • 由于我们查询条件的不确定性,要对查询条件字符串进行拼接。这样不安全和很容易出错。我们就封装了一个查询助手对象。专门用于查询的。

如果您觉得这篇文章帮助到了您,可以给作者一点鼓励