1、现在的绝大多数web应用,通常都以action、service、dao三层去组织代码,这样划分结构很清晰,分工明确
2、一般情况下,我们会把事务控制在service层。
3、action和dao层,会使用一些框架技术。比如action层可能选择有springmvc、struts等,dao层有hibernate、mybatis等选择,所以action的dao有可能遂根据情况变化,而service层关注业务逻辑,业务代码都是自己完成的,代码对自己是透明的。
基于1,我们的每个业务,可能都需要这三层代码,也就是因为一个很简单的业务,我们会写dao层及实现,service层及实现,action层,这样会造成很多的类。所以最好做到dao层必须通用,service层绝大部分通用,这样就会减少大量的类。
基于2,我们应把业务逻辑写在service层,这样才能控制住事务。例如我们的一个业务:删除A记录,插入B记录(需要在一个事务里进行),如果我们把这个业务逻辑写在了action层,我们再action层调用删除A的service,然后再调用插入B的service,如果说插入B失败了,那我们删除A这个操作将不能回滚,因为事务控制在了service层,这样写已不是一个事务。删除A操作的service已经完成,事务已经提交了,插入B的操作在另外的事务里运行。根据需要,业务逻辑尽量放在service层。通过配置spring事务控制的传播行为,在service层可以达到大部分的业务事务要求,而不需另加一层。
基于3,我们更应该把与业务相关的代码移至service层,如果service层的事务不容易控制了,可以增加额外的support层(个人理解,其实还是业务逻辑层)协助控制事务。这主要发生在我们把整个业务逻辑放在了service的一个方法,而这个方法以及调用的方法的事务配置为required(或其他),但业务的部分操作需要在不同的事务中进行,我们不想写另外的方法,也不想去更改事务配置,所以引入support层(或许你说可以把这种逻辑向action层移动,在action层处理,但记住我们的前提,action的框架是会变的,我们想尽量做到更改action层时,更简单。而且从分工来说,action层应该只关注视图逻辑,个人自扫门前雪,休管他人瓦上霜)。support层当然不是只为处理这种事务问题的,我把它定义为业务处理时需要的一些辅助层,可以协助处理业务,比如刚才说的事务问题,还有可以提供工具类支持等。
对于dao层,应该只关注数据库连接执行结果封装这些事。
我们的通用,是基于以上的分析结论进行,下面贴上代码:
通用dao层:
- package com.wls.websvn.dao;
- import java.io.Serializable;
- import java.util.List;
- import java.util.Map;
- import com.wls.websvn.support.Page;
- public interface CommonDao {
- /**
- *
- * 增加一个实体
- * @param pojo
- * @return 影响的行数 0失败,1成功
- */
- public <T extends Serializable> int save(T pojo);
- /**
- *
- * 通过id删除实体
- *
- * @param clazz
- * @param id
- * @return
- */
- public <T extends Serializable> int deleteById(Class<T> clazz,
- Serializable id);
- /**
- *
- * 通过主键获取实体
- *
- * @param clazz
- * @param id
- * @return
- */
- public <T extends Serializable> T getById(Class<T> clazz, Serializable id);
- /**
- *
- * 查询所有实体
- *
- * @param clazz
- * @return
- */
- public <T extends Serializable> List<T> listAll(Class<T> clazz);
- /**
- *
- * 分页查询
- *
- * @param clazz
- * @param p
- * @return
- */
- public <T extends Serializable> Page<T> pageSelect(Class<T> clazz,Page<T> p,String[]attrs,Object[]values);
- /**
- *
- * 分页查询时,用来统计总条数
- *
- * @param clazz
- * @param attrs
- * @param values
- * @return
- */
- public <T extends Serializable> int pageCount(Class<T> clazz,String[]attrs,Object[]values);
- /**
- *
- * 统计总条数
- *
- * @param clazz
- * @return
- */
- public <T extends Serializable> int countAll(Class<T> clazz);
- /**
- *
- * 指定查询使用的命名sql,查询结果封装成map
- *
- * @param statment
- * @param paraMap
- * @return
- */
- List<Map<String,Object>> selectMap(String statment, Map<String, Object> paraMap);
- }
dao层实现:
- package com.wls.websvn.dao.mybatis;
- import java.io.Serializable;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import javax.annotation.Resource;
- import org.apache.commons.lang.StringUtils;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.springframework.stereotype.Repository;
- import com.wls.websvn.dao.CommonDao;
- import com.wls.websvn.support.Page;
- @Repository("commonDao")
- public class CommonDaoImpl implements CommonDao {
- @Resource(name = "sqlSessionFactory")
- protected SqlSessionFactory sqlSessionFactory;
- protected <T> String getStatement(Class<T> clazz, String prefix) {
- String entityName = clazz.getSimpleName();
- if (entityName.endsWith("Model")) {
- entityName = entityName.substring(0, entityName.length() - 5);
- }
- entityName = prefix + entityName;
- return entityName;
- }
- @Override
- public <T extends Serializable> int save(T pojo) {
- String statement = getStatement(pojo.getClass(), "insert");
- return sqlSessionFactory.openSession().insert(statement, pojo);
- }
- @Override
- public <T extends Serializable> int deleteById(Class<T> clazz,
- Serializable id) {
- String statement = getStatement(clazz, "idDelete");
- return sqlSessionFactory.openSession().update(statement, id);
- }
- @Override
- public <T extends Serializable> T getById(Class<T> clazz, Serializable id) {
- String statement = getStatement(clazz, "idGet");
- return sqlSessionFactory.openSession().selectOne(statement, id);
- }
- @Override
- public <T extends Serializable> List<T> listAll(Class<T> clazz) {
- String statement = getStatement(clazz, "list");
- return sqlSessionFactory.openSession().selectList(statement);
- }
- /**
- *
- * 组装排序串
- *
- * @param sort
- * @param order 最好将order定义成枚举类型,传递一个枚举数组
- * @return
- */
- private String genOrderStr(String sort, String order) {
- String orderBy = "";
- if (StringUtils.isNotBlank(sort)) {
- if (StringUtils.isNotBlank(order)) {
- StringBuilder sb = new StringBuilder(" ");
- String[] aSort = sort.split(",");
- String[] aOrder = order.split(",");
- for (int i = 0; i < aSort.length; i++) {
- sb.append(aSort[i]).append(" ");
- if (i < aOrder.length) {
- sb.append(aOrder[i]).append(",");
- } else {
- sb.append("ASC").append(",");
- }
- }
- // 删除最后一个,
- sb.deleteCharAt(sb.length() - 1);
- orderBy = sb.toString();
- } else {
- orderBy = " order by " + sort;
- }
- }
- return orderBy;
- }
- @Override
- public <T extends Serializable> int pageCount(Class<T> clazz,
- String[] attrs, Object[] values) {
- Map<String, Object> paraMap = new HashMap<String, Object>();
- if (values != null && attrs != null) {
- for (int i = 0; i < values.length; i++) {
- if (i < attrs.length) {
- paraMap.put(attrs[i], values[i]);
- }
- }
- }
- String statement = getStatement(clazz, "pageCount");
- Object o = sqlSessionFactory.openSession().selectOne(statement,paraMap);
- return Integer.parseInt(o.toString());
- }
- @Override
- public <T extends Serializable> Page<T> pageSelect(Class<T> clazz,
- Page<T> p, String[] attrs, Object[] values) {
- int startNum = p.getStartIndex();
- int pageSize = p.getPageSize();
- String orderBy = genOrderStr(p.getSort(), p.getOrder());
- Map<String, Object> paraMap = new HashMap<String, Object>();
- if (values != null && attrs != null) {
- for (int i = 0; i < values.length; i++) {
- if (i < attrs.length) {
- paraMap.put(attrs[i], values[i]);
- }
- }
- }
- String statement = getStatement(clazz, "page");
- p.setTotal(pageCount(clazz, attrs, values));
- paraMap.put("startNum", startNum);
- paraMap.put("pageSize", pageSize);
- paraMap.put("endNum", startNum + pageSize);
- paraMap.put("orderBy", orderBy);
- List<T> list = sqlSessionFactory.openSession().selectList(statement,
- paraMap);
- p.setData(list);
- return p;
- }
- @Override
- public <T extends Serializable> int countAll(Class<T> clazz) {
- String statement = getStatement(clazz, "count");
- Object o = sqlSessionFactory.openSession().selectOne(statement);
- return Integer.parseInt(o.toString());
- }
- @Override
- public List<Map<String, Object>> selectMap(String statement,
- Map<String, Object> paraMap) {
- return sqlSessionFactory.openSession().selectList(statement, paraMap);
- }
- }
- package com.wls.websvn.support;
- import java.util.List;
- public class Page<T> {
- /**
- * 页码
- */
- private int page = 1;
- /**
- * 每页条数
- */
- private int pageSize = 10;
- /**
- * 总记录数
- */
- private long total;
- /**
- * 排序列
- */
- private String sort;
- /**
- * 升降序
- */
- private String order;
- /**
- * 单页数据
- */
- private List<T> data;
- public Page() {
- this.page = 1;
- this.pageSize = 10;
- }
- public Page(int page, int pageSize) {
- this.page = page;
- this.pageSize = pageSize;
- }
- /**
- * 获取page值
- * @return int page.
- */
- public int getPage() {
- return page;
- }
- /**
- * 设置page值
- * @param page The page to set.
- */
- public void setPage(int page) {
- this.page = page;
- }
- /**
- * 获取pageSize值
- * @return int pageSize.
- */
- public int getPageSize() {
- return pageSize;
- }
- /**
- * 设置pageSize值
- * @param pageSize The pageSize to set.
- */
- public void setPageSize(int pageSize) {
- this.pageSize = pageSize;
- }
- /**
- * 获取data值
- * @return List<T> data.
- */
- public List<T> getData() {
- return data;
- }
- /**
- * 设置data值
- * @param data The data to set.
- */
- public void setData(List<T> data) {
- this.data = data;
- }
- /**
- * 获取total值
- * @return long total.
- */
- public long getTotal() {
- return total;
- }
- /**
- * 设置total值
- * @param total The total to set.
- */
- public void setTotal(long total) {
- this.total = total;
- }
- public String getSort() {
- return sort;
- }
- public void setSort(String sort) {
- this.sort = sort;
- }
- /**
- * @return the order
- */
- public String getOrder() {
- return order;
- }
- /**
- * @param order the order to set
- */
- public void setOrder(String order) {
- this.order = order;
- }
- /**
- *
- * 获取分页开始的位置
- *
- * @return
- */
- public int getStartIndex() {
- if(page<1){
- return 0;
- }
- return (page - 1) * pageSize;
- }
- }
- package com.wls.websvn.service;
- import java.io.Serializable;
- import java.util.List;
- import java.util.Map;
- import com.wls.websvn.support.Page;
- public interface CommonService {
- /**
- *
- * 增加一个实体
- * @param pojo
- * @return 影响的行数 0失败,1成功
- */
- public <T extends Serializable> int save(T pojo);
- /**
- *
- * 通过id删除实体
- *
- * @param clazz
- * @param id
- * @return
- */
- public <T extends Serializable> int deleteById(Class<T> clazz,
- Serializable id);
- /**
- *
- * 通过主键获取实体
- *
- * @param clazz
- * @param id
- * @return
- */
- public <T extends Serializable> T getById(Class<T> clazz, Serializable id);
- /**
- *
- * 查询所有实体
- *
- * @param clazz
- * @return
- */
- public <T extends Serializable> List<T> listAll(Class<T> clazz);
- /**
- *
- * 分页查询
- *
- * @param clazz
- * @param p
- * @return
- */
- public <T extends Serializable> Page<T> pageSelect(Class<T> clazz,Page<T> p,String[]attrs,Object[]values);
- /**
- *
- * 分页查询时,用来统计总条数
- *
- * @param clazz
- * @param attrs
- * @param values
- * @return
- */
- public <T extends Serializable> int pageCount(Class<T> clazz,String[]attrs,Object[]values);
- /**
- *
- * 统计总条数
- *
- * @param clazz
- * @return
- */
- public <T extends Serializable> int countAll(Class<T> clazz);
- /**
- *
- * 指定查询使用的命名sql,查询结果封装成map
- *
- * @param statment
- * @param paraMap
- * @return
- */
- List<Map<String,Object>> selectMap(String statment, Map<String, Object> paraMap);
- }
- package com.wls.websvn.service.impl;
- import java.io.Serializable;
- import java.util.List;
- import java.util.Map;
- import javax.annotation.Resource;
- import org.springframework.stereotype.Service;
- import com.wls.websvn.dao.CommonDao;
- import com.wls.websvn.service.CommonService;
- import com.wls.websvn.support.Page;
- /**
- *
- * @author weilisky
- * 考虑将来可能切换到hibernate或其他框架的情况,尽量的将切换时不用变的代码移到service层。
- * 将切换时可能会变更的代码放在了dao层
- *
- */
- @Service("commonService")
- public class CommonServiceImpl implements CommonService {
- @Resource(name = "commonDao")
- protected CommonDao commonDao;
- @Override
- public <T extends Serializable> int save(T pojo) {
- return commonDao.save(pojo);
- }
- @Override
- public <T extends Serializable> int deleteById(Class<T> clazz,
- Serializable id) {
- return commonDao.deleteById(clazz, id);
- }
- @Override
- public <T extends Serializable> T getById(Class<T> clazz, Serializable id) {
- return commonDao.getById(clazz, id);
- }
- @Override
- public <T extends Serializable> List<T> listAll(Class<T> clazz) {
- return commonDao.listAll(clazz);
- }
- @Override
- public <T extends Serializable> int pageCount(Class<T> clazz,
- String[] attrs, Object[] values) {
- return commonDao.pageCount(clazz, attrs, values);
- }
- @Override
- public <T extends Serializable> Page<T> pageSelect(Class<T> clazz,
- Page<T> p, String[] attrs, Object[] values) {
- return commonDao.pageSelect(clazz, p, attrs, values);
- }
- @Override
- public <T extends Serializable> int countAll(Class<T> clazz) {
- return commonDao.countAll(clazz);
- }
- @Override
- public List<Map<String, Object>> selectMap(String statement,
- Map<String, Object> paraMap) {
- return commonDao.selectMap(statement, paraMap);
- }
- }
如果CommonService不够用了,你的接口应该继承CommonService接口,你的实现应该继承CommonServiceImpl实现,然后根据需要override其中的一些方法。
需要说明一下,我们把protected <T> String getStatement(Class<T> clazz, String prefix)放置在dao层,因为它并不是业务逻辑的一部分,而且对于mybaits框架,你需要这个方法,而对应hibenate你可能根本不需要(或则可以把这个方法重构到另外的一个接口)。
最后附上例子model和mapping:
- package com.wls.websvn.model;
- import java.io.Serializable;
- import java.util.Date;
- public class BaseModel implements Serializable{
- /**
- *
- */
- private static final long serialVersionUID = -459530011111182045L;
- protected Long id;
- protected Date createDate;
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public Date getCreateDate() {
- return createDate;
- }
- public void setCreateDate(Date createDate) {
- this.createDate = createDate;
- }
- }
- package com.wls.websvn.model;
- public class UserModel extends BaseModel {
- /**
- *
- */
- private static final long serialVersionUID = 5715947400419117755L;
- //登录名
- private String userName;
- //真实姓名
- private String realName;
- //密码
- private String userPwd;
- private String phoneNum;
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getRealName() {
- return realName;
- }
- public void setRealName(String realName) {
- this.realName = realName;
- }
- public String getUserPwd() {
- return userPwd;
- }
- public void setUserPwd(String userPwd) {
- this.userPwd = userPwd;
- }
- public String getPhoneNum() {
- return phoneNum;
- }
- public void setPhoneNum(String phoneNum) {
- this.phoneNum = phoneNum;
- }
- }
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.wls.websvn.model.UserModel">
- <!-- PROCESSDEFINITION INSERT -->
- <insert id="insertUser" parameterType="com.wls.websvn.model.UserModel">
- insert into
- SVN_USER(ID_,
- NAME_, REALNAME_, PASSWORD_, MOBILEPHONE_,CREATE_TIME_)
- values
- (#{id,
- jdbcType=BIGINT},
- #{userName, jdbcType=VARCHAR},
- #{realName,
- jdbcType=VARCHAR},
- #{userPwd, jdbcType=VARCHAR},
- #{phoneNum,
- jdbcType=VARCHAR},
- #{createDate,
- jdbcType=TIMESTAMP})
- </insert>
- <update id="idDeleteUser" parameterType="long">
- DELETE FROM
- SVN_USER WHERE ID_=#{id,jdbcType=BIGINT}
- </update>
- <resultMap id="userResultMap" type="com.wls.websvn.model.UserModel">
- <id property="id" column="ID_" jdbcType="BIGINT" />
- <result property="userName" column="NAME_" jdbcType="VARCHAR" />
- <result property="realName" column="REALNAME_" jdbcType="VARCHAR" />
- <result property="userPwd" column="PASSWORD_" jdbcType="VARCHAR" />
- <result property="phoneNum" column="MOBILEPHONE_" jdbcType="VARCHAR" />
- <result property="createDate" column="CREATE_TIME_" jdbcType="VARCHAR" />
- </resultMap>
- <select id="idGetUser" parameterType="string" resultMap="userResultMap">
- SELECT *
- FROM SVN_USER WHERE ID_=#{id,jdbcType=BIGINT}
- </select>
- <select id="countUser" resultType="int">
- SELECT COUNT(1) FROM
- SVN_USER
- </select>
- <select id="listUser" resultMap="userResultMap">
- SELECT * FROM SVN_USER
- </select>
- <select id="pageCountUser" resultType="int" parameterType="map">
- select count(1) from SVN_USER ST
- <where>
- <if test="id!=null and id!='' ">
- ST.ID_ = #{id}
- </if>
- <if test="name!= null and name!= '' ">
- AND ST.NAME_ LIKE CONCAT(CONCAT('%', #{name}),'%')
- </if>
- <if test="codePrefix !=null and codePrefix!='' ">
- AND ST.CODEPREFIX_ = #{codePrefix}
- </if>
- <if test="active !=null ">
- AND ST.ACTIVE_ = #{active}
- </if>
- <if test="description !=null and description!='' ">
- AND ST.DESCRIPTION_ = #{description}
- </if>
- </where>
- </select>
- <select id="pageUser" resultMap="userResultMap"
- parameterType="map">
- select * from SVN_USER ST
- <where>
- <if test="id!=null and id!='' ">
- ST.ID_ = #{id}
- </if>
- <if test="name!= null and name!= '' ">
- AND ST.NAME_ LIKE CONCAT(CONCAT('%', #{name}),'%')
- </if>
- <if test="codePrefix !=null and codePrefix!='' ">
- AND ST.CODEPREFIX_ = #{codePrefix}
- </if>
- <if test="active !=null ">
- AND ST.ACTIVE_ = #{active}
- </if>
- <if test="description !=null and description!='' ">
- AND ST.DESCRIPTION_ = #{description}
- </if>
- </where>
- <if test="startNum!=null">
- LIMIT ${startNum} , ${pageSize}
- </if>
- <if test="orderBy!=null and orderBy!='' ">
- ${orderBy}
- </if>
- </select>
- <select id="selectMap" resultType="map" parameterType="map">
- SELECT *
- FROM SVN_USER
- </select>
- </mapper>
applicationContext-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 连接池最大数量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/wls/websvn/mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.wls.websvn.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
-->
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
applicationContext-service.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"
- default-lazy-init="true">
- <aop:config>
- <aop:advisor id="managerTx" advice-ref="txAdvice"
- pointcut="execution(* *..service.*Manager.*(..))" order="0" />
- <aop:advisor id="managerTxService" advice-ref="txAdvice"
- pointcut="execution(* *..service.*Service.*(..))" order="1" />
- </aop:config>
- <!-- Enable @Transactional support -->
- <tx:annotation-driven transaction-manager="transactionManager" />
- <!-- Enable @AspectJ support -->
- <aop:aspectj-autoproxy proxy-target-class="true" />
- <!-- Activates scanning of @Autowired -->
- <context:annotation-config />
- <!-- Activates scanning of @Service -->
- <context:component-scan base-package="com.dcfs.qed" />
- <tx:advice id="txAdvice">
- <tx:attributes>
- <tx:method name="get*" read-only="true" />
- <tx:method name="set*" read-only="true" />
- <tx:method name="exist*" read-only="true" />
- <tx:method name="find*" read-only="true" />
- <tx:method name="load*" read-only="true" />
- <tx:method name="query*" read-only="true" />
- <tx:method name="list*" read-only="true" />
- <tx:method name="paging*" read-only="true" />
- <tx:method name="update*" propagation="REQUIRED"
- rollback-for="Exception" />
- <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception" />
- <tx:method name="remove*" propagation="REQUIRED"
- rollback-for="Exception" />
- <tx:method name="save*" propagation="REQUIRED"
- rollback-for="Exception" />
- <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" />
- <tx:method name="create*" propagation="REQUIRED"
- rollback-for="Exception" />
- </tx:attributes>
- </tx:advice>
- </beans>