分层架构下的纯JDBC事务控制简单解决方案

对目前的JavaEE企业应用开发来说,基本都会采用分层的架构, 这样可以分散关注、松散耦合、逻辑复用、标准定义。例如,目前使用SSH组合时典型的四层架构:表示层、业务层、持久层和数据层;那么,在四层架构中,事务的控制应该放在哪一层呢?


如果使用Spring框架,它对事务做了很好的封装,通过它的AOP配置,可以灵活的配置在任何一层;但是在很多的需求和应用,直接使用JDBC事务控制还是有其优势的。所以,本文来讨论纯JDBC事务的控制问题。

其实,事务是以业务逻辑为基础的;一个完整的业务应该对应业务层里的一个方法;如果业务操作失败,则整个事务回滚;所以,事务控制是绝对应该放在业务层的;但是,持久层的设计应该遵循一个很重要的原则:持久层应该保证操作的原子性,就是说持久层里的每个方法都应该是不可以分割的。

例如针对一个部门和员工的CRUD操作。如果要删除某个部门,就应该在DeptDao中有一个删除部门的方法:
public class DeptDao {
    public void deleteDept(int id) ;         //删除指定ID的部门
}
在EmpDao中有一个删除指定部门下的所有员工的方法
public interface EmpDao{
    public void deleteEmpByDeptId(int id);    //删除指定部门下的所有员工
}
这样,就应该在业务层DeptService中的删除部门方法中组合这两个方法,即把它们放置在同一个事务中:
public class DeptService{
    public void deleteDept(int id){
        try{
             //启动JDBC事务
             //调用EmpDao中的deleteEmpByDeptId(id)方法
             //调用DeptDao中的deleteDept(id)方法
             //操作正常,提交事务
        }catch(Exception e){
             //异常,回滚事务
        }
    }
}

要让这两个Dao操作在同一个事务,最主要的一点就是:启动JDBC事务中使用的数据库连接和两个Dao操作方法里获得的数据库连接要是同一个连接。参照Spring的JDBC事务原理,可以使用ThreadLocal类, 在启动JDBC事务中把数据库连接绑定到线程,以保证在同一个线程下获得的都是同一个连接。这样就达到目的了。

如下数据库工具类:

view plaincopy to clipboardprint?
package com.tjitcast.common;  
import java.io.IOException;  
import java.sql.Connection;  
import java.sql.SQLException;  
import java.util.Properties;  
import javax.sql.DataSource;  
import com.mchange.v2.c3p0.DataSources;  
import com.tjitcast.dao.DaoException;  
/** 
 *  数据库工具类 
 *  可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上 
 *  可以获取JDBC的事务管理器 
 * @author qiujy 
 * @version 0.9Beta 
 */ 
public class DbUtils {  
    private static Properties prop = new Properties();  
    /** 数据源 */ 
    private static DataSource ds = null;   
      
    //用来把Connection绑定到当前线程上的变量  
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();  
    static{  
        try {  
            prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));  
        } catch (IOException e) {  
            e.printStackTrace();  
            System.out.println("在classpath下没有找到jdbc.properties文件");  
        }  
          
        //使用C3P0连接池技术  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
              
            DataSource unpooled = DataSources.unpooledDataSource(  
                    prop.getProperty("url"),  
                    prop.getProperty("user"),  
                    prop.getProperty("password"));  
            ds = DataSources.pooledDataSource(unpooled);  
              
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
    }  
      
    private DbUtils(){}  
      
    /** 
     * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上 
     * @return 成功,返回Connection对象,否则返回null 
     */ 
    public static synchronized Connection getConnection(){  
        Connection conn = tl.get(); //先从当前线程上取出连接实例  
          
        if(null == conn){ //如果当前线程上没有Connection的实例   
            try {  
                conn = ds.getConnection(); // 从连接池中取出一个连接实例   
                tl.set(conn);  //把它绑定到当前线程上  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
        return conn;  
    }  
    /** 
     * 获取事务管理器 
     * @return 事务管理实例 
     */ 
    public static synchronized TransactionManager getTranManager(){  
        return new TransactionManager(getConnection());  
    }  
      
    /** 
     * 关闭数据库连接,并卸装线程绑定 
     * @param conn 要关闭数据库连接实例 
     * @throws DaoException  
     */ 
    protected static void close(Connection conn) throws DaoException{  
        if(conn != null){  
            try {  
                conn.close();  
            } catch (SQLException e) {  
                throw new DaoException("关闭连接时出现异常",e);  
            } finally {  
                    tl.remove(); //卸装线程绑定  
            }  
        }  
    }  
      

package com.tjitcast.common;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.DataSources;
import com.tjitcast.dao.DaoException;
/**
 *  数据库工具类
 *  可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上
 *  可以获取JDBC的事务管理器
 * @author qiujy
 * @version 0.9Beta
 */
public class DbUtils {
 private static Properties prop = new Properties();
 /** 数据源 */
 private static DataSource ds = null;
 
 //用来把Connection绑定到当前线程上的变量
 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
 static{
  try {
   prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));
  } catch (IOException e) {
   e.printStackTrace();
   System.out.println("在classpath下没有找到jdbc.properties文件");
  }
  
  //使用C3P0连接池技术
  try {
   Class.forName("com.mysql.jdbc.Driver");
   
   DataSource unpooled = DataSources.unpooledDataSource(
     prop.getProperty("url"),
     prop.getProperty("user"),
     prop.getProperty("password"));
   ds = DataSources.pooledDataSource(unpooled);
   
  } catch (ClassNotFoundException e) {
   e.printStackTrace();
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }
 
 private DbUtils(){}
 
 /**
  * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上
  * @return 成功,返回Connection对象,否则返回null
  */
 public static synchronized Connection getConnection(){
  Connection conn = tl.get(); //先从当前线程上取出连接实例
  
  if(null == conn){ //如果当前线程上没有Connection的实例
   try {
    conn = ds.getConnection(); // 从连接池中取出一个连接实例
    tl.set(conn);  //把它绑定到当前线程上
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }
  return conn;
 }
 /**
  * 获取事务管理器
  * @return 事务管理实例
  */
 public static synchronized TransactionManager getTranManager(){
  return new TransactionManager(getConnection());
 }
 
 /**
  * 关闭数据库连接,并卸装线程绑定
  * @param conn 要关闭数据库连接实例
  * @throws DaoException
  */
 protected static void close(Connection conn) throws DaoException{
  if(conn != null){
   try {
    conn.close();
   } catch (SQLException e) {
    throw new DaoException("关闭连接时出现异常",e);
   } finally {
           tl.remove(); //卸装线程绑定
   }
  }
 }
 
}
 

如下事务管理器类:

package com.tjitcast.common;  
import java.sql.Connection;  
import java.sql.SQLException;  
import com.tjitcast.dao.DaoException;  
/** 
 * 事务管理器 
 * @author qiujy 
 * @version 0.9Beta 
 */ 
public class TransactionManager {  
    private Connection conn;  
      
    protected TransactionManager(Connection conn) {  
        this.conn = conn;  
    }  
      
    /** 开启事务 */ 
    public void beginTransaction() throws DaoException{  
        try {  
            conn.setAutoCommit(false);  //把事务提交方式改为手工提交  
        } catch (SQLException e) {  
            throw new DaoException("开户事务时出现异常",e);  
        }  
    }  
      
    /** 提交事务并关闭连接 */ 
    public void commitAndClose() throws DaoException{  
        try {  
            conn.commit(); //提交事务  
        } catch (SQLException e) {  
            throw new DaoException("提交事务时出现异常",e);  
        }finally{  
            DbUtils.close(conn);  
        }  
    }  
      
    /** 回滚并关闭连接 */ 
    public void rollbackAndClose()throws DaoException{  
        try {  
            conn.rollback();  
        } catch (SQLException e) {  
            throw new DaoException("回滚事务时出现异常",e);  
        }finally{  
            DbUtils.close(conn);  
        }  
    }  

package com.tjitcast.common;
import java.sql.Connection;
import java.sql.SQLException;
import com.tjitcast.dao.DaoException;
/**
 * 事务管理器
 * @author qiujy
 * @version 0.9Beta
 */
public class TransactionManager {
 private Connection conn;
 
 protected TransactionManager(Connection conn) {
  this.conn = conn;
 }
 
 /** 开启事务 */
 public void beginTransaction() throws DaoException{
  try {
   conn.setAutoCommit(false);  //把事务提交方式改为手工提交
  } catch (SQLException e) {
   throw new DaoException("开户事务时出现异常",e);
  }
 }
 
 /** 提交事务并关闭连接 */
 public void commitAndClose() throws DaoException{
  try {
   conn.commit(); //提交事务
  } catch (SQLException e) {
   throw new DaoException("提交事务时出现异常",e);
  }finally{
   DbUtils.close(conn);
  }
 }
 
 /** 回滚并关闭连接 */
 public void rollbackAndClose()throws DaoException{
  try {
   conn.rollback();
  } catch (SQLException e) {
   throw new DaoException("回滚事务时出现异常",e);
  }finally{
   DbUtils.close(conn);
  }
 }
}
 

如下业务层类:

view plaincopy to clipboardprint?
package com.tjitcast.service;  
import java.util.List;  
import com.tjitcast.common.DbUtils;  
import com.tjitcast.common.TransactionManager;  
import com.tjitcast.dao.DaoException;  
import com.tjitcast.dao.DaoFactory;  
import com.tjitcast.dao.DeptDao;  
import com.tjitcast.dao.EmployeeDao;  
import com.tjitcast.entity.Dept;  
import com.tjitcast.entity.Employee;  
import com.tjitcast.entity.PageModel;  
/** 
 * 业务层门面  --> 添加事务控制 
 * @author qiujy 
 */ 
public class ServiceFacade {  
    private DeptDao deptDao = DaoFactory.getInstance("deptDao", DeptDao.class);  
    private EmployeeDao empDao  = DaoFactory.getInstance("empDao", EmployeeDao.class);  
      
    /** 
     * 新增部门 
     * @param dept 
     */ 
    public void insertDept(Dept dept){  
        TransactionManager tx = DbUtils.getTranManager();  
        try{  
            tx.beginTransaction();  
              
            deptDao.insert(dept);  
              
            tx.commitAndClose();  
        }catch (DaoException e) {  
            tx.rollbackAndClose();  
        }  
    }  
      
    /** 
     * 新增员工 
     * @param emp 员工 
     */ 
    public void insertEmp(Employee emp){  
        TransactionManager tx = DbUtils.getTranManager();  
        try{  
            tx.beginTransaction();  
              
            empDao.insert(emp);  
              
            tx.commitAndClose();  
        }catch (DaoException e) {  
            tx.rollbackAndClose();  
        }  
    }  
      
    /** 
     * 获取所有部门的列表 
     * @return 部门列表 
     */ 
    public List<Dept> getDeptList(){  
        List<Dept> list = null;  
          
        TransactionManager tx = DbUtils.getTranManager();  
        try{  
            tx.beginTransaction();  
              
            list = deptDao.getDeptList();  
              
            tx.commitAndClose();  
        }catch (DaoException e) {  
            e.printStackTrace();  
            tx.rollbackAndClose();  
        }  
          
        return list;  
    }  
      
    /** 
     * 获取指定部门下的员工分页列表 
     * @param deptId 
     * @param pageNo 
     * @param pageSize 
     * @return 符合条件的PageModel 
     */ 
    public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){  
        PageModel<Employee> pm = null;   
        TransactionManager tx = DbUtils.getTranManager();  
        try{  
            tx.beginTransaction();  
              
            pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);  
              
            tx.commitAndClose();  
        }catch (DaoException e) {  
            tx.rollbackAndClose();  
        }  
        return pm;  
    }  
      
    /** 
     * 删除指定ID的部门 
     * @param id 部门ID 
     */ 
    public void deleteDept(int id){  
          
        TransactionManager tx = DbUtils.getTranManager();  
        try{  
            tx.beginTransaction();  
              
            empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工  
            deptDao.delete(id);  //再删除该部门  
              
            tx.commitAndClose();  
        }catch (DaoException e) {  
            tx.rollbackAndClose();  
        }  
    }  

package com.tjitcast.service;
import java.util.List;
import com.tjitcast.common.DbUtils;
import com.tjitcast.common.TransactionManager;
import com.tjitcast.dao.DaoException;
import com.tjitcast.dao.DaoFactory;
import com.tjitcast.dao.DeptDao;
import com.tjitcast.dao.EmployeeDao;
import com.tjitcast.entity.Dept;
import com.tjitcast.entity.Employee;
import com.tjitcast.entity.PageModel;
/**
 * 业务层门面  --> 添加事务控制
 * @author qiujy
 */
public class ServiceFacade {
 private DeptDao deptDao = DaoFactory.getInstance("deptDao", DeptDao.class);
 private EmployeeDao empDao  = DaoFactory.getInstance("empDao", EmployeeDao.class);
 
 /**
  * 新增部门
  * @param dept
  */
 public void insertDept(Dept dept){
  TransactionManager tx = DbUtils.getTranManager();
  try{
   tx.beginTransaction();
   
   deptDao.insert(dept);
   
   tx.commitAndClose();
  }catch (DaoException e) {
   tx.rollbackAndClose();
  }
 }
 
 /**
  * 新增员工
  * @param emp 员工
  */
 public void insertEmp(Employee emp){
  TransactionManager tx = DbUtils.getTranManager();
  try{
   tx.beginTransaction();
   
   empDao.insert(emp);
   
   tx.commitAndClose();
  }catch (DaoException e) {
   tx.rollbackAndClose();
  }
 }
 
 /**
  * 获取所有部门的列表
  * @return 部门列表
  */
 public List<Dept> getDeptList(){
  List<Dept> list = null;
  
  TransactionManager tx = DbUtils.getTranManager();
  try{
   tx.beginTransaction();
   
   list = deptDao.getDeptList();
   
   tx.commitAndClose();
  }catch (DaoException e) {
   e.printStackTrace();
   tx.rollbackAndClose();
  }
  
  return list;
 }
 
 /**
  * 获取指定部门下的员工分页列表
  * @param deptId
  * @param pageNo
  * @param pageSize
  * @return 符合条件的PageModel
  */
 public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){
  PageModel<Employee> pm = null;
  TransactionManager tx = DbUtils.getTranManager();
  try{
   tx.beginTransaction();
   
   pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);
   
   tx.commitAndClose();
  }catch (DaoException e) {
   tx.rollbackAndClose();
  }
  return pm;
 }
 
 /**
  * 删除指定ID的部门
  * @param id 部门ID
  */
 public void deleteDept(int id){
  
  TransactionManager tx = DbUtils.getTranManager();
  try{
   tx.beginTransaction();
   
   empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工
   deptDao.delete(id);  //再删除该部门
   
   tx.commitAndClose();
  }catch (DaoException e) {
   tx.rollbackAndClose();
  }
 }
}

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qjyong/archive/2010/04/08/5464835.aspx

你可能感兴趣的:(DAO,spring,sql,jdbc,配置管理)