您的位置:首页 > 运维架构 > 网站架构

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

2013-08-18 11:05 495 查看


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

 

如下数据库工具类:

[java] view
plaincopy

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(); //卸装线程绑定  

            }  

        }  

    }  

      

}  

 

如下事务管理器类:

[java] view
plaincopy

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);  

        }  

    }  

}  

 

如下业务层类:

[java] view
plaincopy

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();  

        }  

    }  

}  

 

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



 

如有需要,留下邮箱。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JDBC 事务