• 分层架构下的纯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事务中把数据库连接绑定到线程,以保证在同一个线程下获得的都是同一个连接。这样就达到目的了。

    如下数据库工具类:

    1. package com.tjitcast.common;  
    2. import java.io.IOException;  
    3. import java.sql.Connection;  
    4. import java.sql.SQLException;  
    5. import java.util.Properties;  
    6. import javax.sql.DataSource;  
    7. import com.mchange.v2.c3p0.DataSources;  
    8. import com.tjitcast.dao.DaoException;  
    9. /** 
    10.  *  数据库工具类 
    11.  *  可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上 
    12.  *  可以获取JDBC的事务管理器 
    13.  * @author qiujy 
    14.  * @version 0.9Beta 
    15.  */  
    16. public class DbUtils {  
    17.     private static Properties prop = new Properties();  
    18.     /** 数据源 */  
    19.     private static DataSource ds = null;   
    20.       
    21.     //用来把Connection绑定到当前线程上的变量  
    22.     private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();  
    23.     static{  
    24.         try {  
    25.             prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));  
    26.         } catch (IOException e) {  
    27.             e.printStackTrace();  
    28.             System.out.println("在classpath下没有找到jdbc.properties文件");  
    29.         }  
    30.           
    31.         //使用C3P0连接池技术  
    32.         try {  
    33.             Class.forName("com.mysql.jdbc.Driver");  
    34.               
    35.             DataSource unpooled = DataSources.unpooledDataSource(  
    36.                     prop.getProperty("url"),  
    37.                     prop.getProperty("user"),  
    38.                     prop.getProperty("password"));  
    39.             ds = DataSources.pooledDataSource(unpooled);  
    40.               
    41.         } catch (ClassNotFoundException e) {  
    42.             e.printStackTrace();  
    43.         } catch (SQLException e) {  
    44.             e.printStackTrace();  
    45.         }  
    46.     }  
    47.       
    48.     private DbUtils(){}  
    49.       
    50.     /** 
    51.      * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上 
    52.      * @return 成功,返回Connection对象,否则返回null 
    53.      */  
    54.     public static synchronized Connection getConnection(){  
    55.         Connection conn = tl.get(); //先从当前线程上取出连接实例  
    56.           
    57.         if(null == conn){ //如果当前线程上没有Connection的实例   
    58.             try {  
    59.                 conn = ds.getConnection(); // 从连接池中取出一个连接实例   
    60.                 tl.set(conn);  //把它绑定到当前线程上  
    61.             } catch (SQLException e) {  
    62.                 e.printStackTrace();  
    63.             }  
    64.         }  
    65.         return conn;  
    66.     }  
    67.     /** 
    68.      * 获取事务管理器 
    69.      * @return 事务管理实例 
    70.      */  
    71.     public static synchronized TransactionManager getTranManager(){  
    72.         return new TransactionManager(getConnection());  
    73.     }  
    74.       
    75.     /** 
    76.      * 关闭数据库连接,并卸装线程绑定 
    77.      * @param conn 要关闭数据库连接实例 
    78.      * @throws DaoException  
    79.      */  
    80.     protected static void close(Connection conn) throws DaoException{  
    81.         if(conn != null){  
    82.             try {  
    83.                 conn.close();  
    84.             } catch (SQLException e) {  
    85.                 throw new DaoException("关闭连接时出现异常",e);  
    86.             } finally {  
    87.                     tl.remove(); //卸装线程绑定  
    88.             }  
    89.         }  
    90.     }  
    91.       
    92. }  

    如下事务管理器类:

    1. package com.tjitcast.common;  
    2. import java.sql.Connection;  
    3. import java.sql.SQLException;  
    4. import com.tjitcast.dao.DaoException;  
    5. /** 
    6.  * 事务管理器 
    7.  * @author qiujy 
    8.  * @version 0.9Beta 
    9.  */  
    10. public class TransactionManager {  
    11.     private Connection conn;  
    12.       
    13.     protected TransactionManager(Connection conn) {  
    14.         this.conn = conn;  
    15.     }  
    16.       
    17.     /** 开启事务 */  
    18.     public void beginTransaction() throws DaoException{  
    19.         try {  
    20.             conn.setAutoCommit(false);  //把事务提交方式改为手工提交  
    21.         } catch (SQLException e) {  
    22.             throw new DaoException("开户事务时出现异常",e);  
    23.         }  
    24.     }  
    25.       
    26.     /** 提交事务并关闭连接 */  
    27.     public void commitAndClose() throws DaoException{  
    28.         try {  
    29.             conn.commit(); //提交事务  
    30.         } catch (SQLException e) {  
    31.             throw new DaoException("提交事务时出现异常",e);  
    32.         }finally{  
    33.             DbUtils.close(conn);  
    34.         }  
    35.     }  
    36.       
    37.     /** 回滚并关闭连接 */  
    38.     public void rollbackAndClose()throws DaoException{  
    39.         try {  
    40.             conn.rollback();  
    41.         } catch (SQLException e) {  
    42.             throw new DaoException("回滚事务时出现异常",e);  
    43.         }finally{  
    44.             DbUtils.close(conn);  
    45.         }  
    46.     }  
    47. }  

    如下业务层类:

    1. package com.tjitcast.service;  
    2. import java.util.List;  
    3. import com.tjitcast.common.DbUtils;  
    4. import com.tjitcast.common.TransactionManager;  
    5. import com.tjitcast.dao.DaoException;  
    6. import com.tjitcast.dao.DaoFactory;  
    7. import com.tjitcast.dao.DeptDao;  
    8. import com.tjitcast.dao.EmployeeDao;  
    9. import com.tjitcast.entity.Dept;  
    10. import com.tjitcast.entity.Employee;  
    11. import com.tjitcast.entity.PageModel;  
    12. /** 
    13.  * 业务层门面  --> 添加事务控制 
    14.  * @author qiujy 
    15.  */  
    16. public class ServiceFacade {  
    17.     private DeptDao deptDao = DaoFactory.getInstance("deptDao", DeptDao.class);  
    18.     private EmployeeDao empDao  = DaoFactory.getInstance("empDao", EmployeeDao.class);  
    19.       
    20.     /** 
    21.      * 新增部门 
    22.      * @param dept 
    23.      */  
    24.     public void insertDept(Dept dept){  
    25.         TransactionManager tx = DbUtils.getTranManager();  
    26.         try{  
    27.             tx.beginTransaction();  
    28.               
    29.             deptDao.insert(dept);  
    30.               
    31.             tx.commitAndClose();  
    32.         }catch (DaoException e) {  
    33.             tx.rollbackAndClose();  
    34.         }  
    35.     }  
    36.       
    37.     /** 
    38.      * 新增员工 
    39.      * @param emp 员工 
    40.      */  
    41.     public void insertEmp(Employee emp){  
    42.         TransactionManager tx = DbUtils.getTranManager();  
    43.         try{  
    44.             tx.beginTransaction();  
    45.               
    46.             empDao.insert(emp);  
    47.               
    48.             tx.commitAndClose();  
    49.         }catch (DaoException e) {  
    50.             tx.rollbackAndClose();  
    51.         }  
    52.     }  
    53.       
    54.     /** 
    55.      * 获取所有部门的列表 
    56.      * @return 部门列表 
    57.      */  
    58.     public List<Dept> getDeptList(){  
    59.         List<Dept> list = null;  
    60.           
    61.         TransactionManager tx = DbUtils.getTranManager();  
    62.         try{  
    63.             tx.beginTransaction();  
    64.               
    65.             list = deptDao.getDeptList();  
    66.               
    67.             tx.commitAndClose();  
    68.         }catch (DaoException e) {  
    69.             e.printStackTrace();  
    70.             tx.rollbackAndClose();  
    71.         }  
    72.           
    73.         return list;  
    74.     }  
    75.       
    76.     /** 
    77.      * 获取指定部门下的员工分页列表 
    78.      * @param deptId 
    79.      * @param pageNo 
    80.      * @param pageSize 
    81.      * @return 符合条件的PageModel 
    82.      */  
    83.     public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){  
    84.         PageModel<Employee> pm = null;   
    85.         TransactionManager tx = DbUtils.getTranManager();  
    86.         try{  
    87.             tx.beginTransaction();  
    88.               
    89.             pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);  
    90.               
    91.             tx.commitAndClose();  
    92.         }catch (DaoException e) {  
    93.             tx.rollbackAndClose();  
    94.         }  
    95.         return pm;  
    96.     }  
    97.       
    98.     /** 
    99.      * 删除指定ID的部门 
    100.      * @param id 部门ID 
    101.      */  
    102.     public void deleteDept(int id){  
    103.           
    104.         TransactionManager tx = DbUtils.getTranManager();  
    105.         try{  
    106.             tx.beginTransaction();  
    107.               
    108.             empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工  
    109.             deptDao.delete(id);  //再删除该部门  
    110.               
    111.             tx.commitAndClose();  
    112.         }catch (DaoException e) {  
    113.             tx.rollbackAndClose();  
    114.         }  
    115.     }  
    116. }  

    具体的示例代码结构如下(Eclipse工程):

  • 相关阅读:
    Nmap笔记
    Spring AOP(一)
    Spring IOC(三)
    Spring IOC(二)
    Spring IOC(一)
    bootstrap 使用(三)
    bootstrap 使用(二)
    bootstrap 使用(一)
    js(二)
    QQ邮件
  • 原文地址:https://www.cnblogs.com/zaifeng0108/p/7225065.html
Copyright © 2020-2023  润新知