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

http://blog.csdn.net/qjyong/article/details/5464835

对目前的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

  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. //用来把Connection绑定到当前线程上的变量
  21. private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
  22. static{
  23. try {
  24. prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. System.out.println("在classpath下没有找到jdbc.properties文件");
  28. }
  29. //使用C3P0连接池技术
  30. try {
  31. Class.forName("com.mysql.jdbc.Driver");
  32. DataSource unpooled = DataSources.unpooledDataSource(
  33. prop.getProperty("url"),
  34. prop.getProperty("user"),
  35. prop.getProperty("password"));
  36. ds = DataSources.pooledDataSource(unpooled);
  37. } catch (ClassNotFoundException e) {
  38. e.printStackTrace();
  39. } catch (SQLException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. private DbUtils(){}
  44. /**
  45. * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上
  46. * @return 成功,返回Connection对象,否则返回null
  47. */
  48. public static synchronized Connection getConnection(){
  49. Connection conn = tl.get(); //先从当前线程上取出连接实例
  50. if(null == conn){ //如果当前线程上没有Connection的实例
  51. try {
  52. conn = ds.getConnection(); // 从连接池中取出一个连接实例
  53. tl.set(conn);  //把它绑定到当前线程上
  54. } catch (SQLException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. return conn;
  59. }
  60. /**
  61. * 获取事务管理器
  62. * @return 事务管理实例
  63. */
  64. public static synchronized TransactionManager getTranManager(){
  65. return new TransactionManager(getConnection());
  66. }
  67. /**
  68. * 关闭数据库连接,并卸装线程绑定
  69. * @param conn 要关闭数据库连接实例
  70. * @throws DaoException
  71. */
  72. protected static void close(Connection conn) throws DaoException{
  73. if(conn != null){
  74. try {
  75. conn.close();
  76. } catch (SQLException e) {
  77. throw new DaoException("关闭连接时出现异常",e);
  78. } finally {
  79. tl.remove(); //卸装线程绑定
  80. }
  81. }
  82. }
  83. }

如下事务管理器类:

[java] view plaincopy

  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. protected TransactionManager(Connection conn) {
  13. this.conn = conn;
  14. }
  15. /** 开启事务 */
  16. public void beginTransaction() throws DaoException{
  17. try {
  18. conn.setAutoCommit(false);  //把事务提交方式改为手工提交
  19. } catch (SQLException e) {
  20. throw new DaoException("开户事务时出现异常",e);
  21. }
  22. }
  23. /** 提交事务并关闭连接 */
  24. public void commitAndClose() throws DaoException{
  25. try {
  26. conn.commit(); //提交事务
  27. } catch (SQLException e) {
  28. throw new DaoException("提交事务时出现异常",e);
  29. }finally{
  30. DbUtils.close(conn);
  31. }
  32. }
  33. /** 回滚并关闭连接 */
  34. public void rollbackAndClose()throws DaoException{
  35. try {
  36. conn.rollback();
  37. } catch (SQLException e) {
  38. throw new DaoException("回滚事务时出现异常",e);
  39. }finally{
  40. DbUtils.close(conn);
  41. }
  42. }
  43. }

如下业务层类:

[java] view plaincopy

  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. * @param dept
  22. */
  23. public void insertDept(Dept dept){
  24. TransactionManager tx = DbUtils.getTranManager();
  25. try{
  26. tx.beginTransaction();
  27. deptDao.insert(dept);
  28. tx.commitAndClose();
  29. }catch (DaoException e) {
  30. tx.rollbackAndClose();
  31. }
  32. }
  33. /**
  34. * 新增员工
  35. * @param emp 员工
  36. */
  37. public void insertEmp(Employee emp){
  38. TransactionManager tx = DbUtils.getTranManager();
  39. try{
  40. tx.beginTransaction();
  41. empDao.insert(emp);
  42. tx.commitAndClose();
  43. }catch (DaoException e) {
  44. tx.rollbackAndClose();
  45. }
  46. }
  47. /**
  48. * 获取所有部门的列表
  49. * @return 部门列表
  50. */
  51. public List<Dept> getDeptList(){
  52. List<Dept> list = null;
  53. TransactionManager tx = DbUtils.getTranManager();
  54. try{
  55. tx.beginTransaction();
  56. list = deptDao.getDeptList();
  57. tx.commitAndClose();
  58. }catch (DaoException e) {
  59. e.printStackTrace();
  60. tx.rollbackAndClose();
  61. }
  62. return list;
  63. }
  64. /**
  65. * 获取指定部门下的员工分页列表
  66. * @param deptId
  67. * @param pageNo
  68. * @param pageSize
  69. * @return 符合条件的PageModel
  70. */
  71. public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){
  72. PageModel<Employee> pm = null;
  73. TransactionManager tx = DbUtils.getTranManager();
  74. try{
  75. tx.beginTransaction();
  76. pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);
  77. tx.commitAndClose();
  78. }catch (DaoException e) {
  79. tx.rollbackAndClose();
  80. }
  81. return pm;
  82. }
  83. /**
  84. * 删除指定ID的部门
  85. * @param id 部门ID
  86. */
  87. public void deleteDept(int id){
  88. TransactionManager tx = DbUtils.getTranManager();
  89. try{
  90. tx.beginTransaction();
  91. empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工
  92. deptDao.delete(id);  //再删除该部门
  93. tx.commitAndClose();
  94. }catch (DaoException e) {
  95. tx.rollbackAndClose();
  96. }
  97. }
  98. }

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

时间: 2024-11-08 19:27:29

分层架构下的纯JDBC事务控制简单解决方案【转】的相关文章

JDBC事务控制

JDBC事务控制事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功.例如转账,一方多了,一方少了同时执行.mysql数据库默认事务是自动提交的,即一条SQL语句就是一个单独的事务,是自动提交的.Oracle数据库默认是事务不是自动提交的,没一句都需要手动提交事务,否则默认为都是同一个事务.对于多条sql放在同一个事务中需要使用事务命令start transaction 开启事务(所有增删改查都在临时表中进行) Rollback 回滚事务(取消操作) Commit 提交事

JDBC 事务控制

一.简介: 前面一遍提到了jdbc事务相关的概念.从中了解到事务应具有ACID特性.所以对于javaweb开发来说,某一个service层的方法,应该是一个事务,应该是具有原子性的.特别是当一个service方法中需要调用多次dao层的方法.应该必须要保证,这些多次调用的dao方法必须是要不全部执行成功.要不全部执行失败.比如说银行业务的service方法的转账方法,需要通过dao调用对源转账户信息进行更新减少指定金额,然后调用dao对目标账户信息进行更新增加指定金额. 那么如下保证在跨dao层

【转】JDBC事务控制管理

转载声明:本文转载自csdn博客,URL=http://blog.csdn.net/caomiao2006/article/details/22412755 1.事务 (1)事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A——B转帐,对应于如下两条sql语句 update account set money=money-100 where name=‘a’; update account set money=money+100 where na

JDBC事务控制管理

1.事务 (1)事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A——B转帐,对应于如下两条sql语句 update account set money=money-100 where name=‘a’; update account set money=money+100 where name=‘b’; 数据库默认事务是自动提交的,也就是发一条sql它就执行一条.如果想多条sql放在一个事务中执行,则需要使用如下语句. (2)数据库开启事务命令

JDBC事务的简单使用

在实际功能当中,经常会碰到同时对一组数据进行增加和减少,最常见的就是交易功能. 事务内执行的语句,要么都成功,要么都失败,如果有一句没执行成功,整个事务都不会提交的. import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class JDBC_transactions { public static voi

jdbc 事务简单例子

 一.什么是Java事务 通常的观念认为,事务仅与数据库相关. 事务必须服从ISO/IEC所制定的ACID原则.ACID是原子性(atomicity).一致性(consistency).隔离性(isolation)和持久性(durability)的缩写.事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效.一致性表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态.隔离性表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见.持久性表示已提交的

ActiveX数据对象之事务控制在VB和DELPHI中的应用

本文发表在中国人民解放军"信息工程大学"学报 2001年第3期. ActiveX数据对象之事务控制在VB和DELPHI中的应用                     马根峰1   ,  孙艳2  , 宋伟1                       ( 1.重庆邮电学院 ,重庆,400065 :2. 铁道部第十九工程局四处,通辽,028000  ) 摘要      事务控制是数据库应用系统中的关键技术之一,本文一开始先对事务控制的概念以及微软的 ActiveX数据对象(ADO)的事

JAVAWEB开发之事务详解(mysql与JDBC下使用方法、事务的特性、锁机制)和连接池的详细使用(dbcp以d3p0)

事务简介 事务的概念:事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功 在开发中,有事务的存在,可以保证数据的完整性. 注意:数据库默认事务是自动提交的,也就是发一条SQL 就执行一条.如果想多条SQL语句放在一个事务中执行,需要添加事务有关的语句. 如何开启事务? 事务的操作方式: 创建表: create table account( id int primary key auto_increment, name varchar(20), money double

深入探索spring技术内幕(八): Spring +JDBC组合开发和事务控制

一. 配置数据源 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/> <property name="