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

如下数据库工具类:

  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. }

如下事务管理器类:

  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. }

如下业务层类:

  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工程):

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

  1. JDBC事务控制管理(转载)

    JDBC事务控制管理 转载于 2018年01月26日 15:46:11 1.事务 (1)事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A——B转帐, ...

  2. JDBC 事务控制

    一.简介: 前面一遍提到了jdbc事务相关的概念.从中了解到事务应具有ACID特性.所以对于javaweb开发来说,某一个service层的方法,应该是一个事务,应该是具有原子性的.特别是当一个ser ...

  3. JDBC事务控制管理

    1.事务 (1)事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A——B转帐,对应于如下两条sql语句 update account set mone ...

  4. JDBC事务控制

    概念 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit).事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并 ...

  5. JDBC事务的简单使用

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

  6. 事务和JDBC事务隔离级别

    与事务相关的理论 mysql事物隔离级别:http://mj4d.iteye.com/blog/1744276 事务(Transaction): 是并发控制的单元,是用户定义的一个操作序列.这些操作要 ...

  7. jdbc事务、连接池概念、c3p0、Driud、JDBC Template、DBUtils

    JDBC 事务控制 什么是事务:一个包含多个步骤或者业务操作.如果这个业务或者多个步骤被事务管理,则这多个步骤要么同时成功,要么回滚(多个步骤同时执行失败),这多个步骤是一个整体,不可分割的. 操作: ...

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

            本文发表在中国人民解放军"信息工程大学"学报 2001年第3期.        ActiveX数据对象之事务控制在VB和DELPHI中的应用             ...

  9. 一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.分布式架构下,Session共享有什么方案 2.简述你对RPC.RMI的理解 3.分布式id生成方案 4.分布式锁解决 ...

随机推荐

  1. 美团HD(9)-监听点击城市

    DJSelectCityViewController.h // 点击城市发出通知 - (void)tableView:(UITableView *)tableView didSelectRowAtIn ...

  2. 83. Remove Duplicates from Sorted List【easy】

    83. Remove Duplicates from Sorted List[easy] Given a sorted linked list, delete all duplicates such ...

  3. 141. Sqrt(x) 【easy】

    141. Sqrt(x) [easy] Implement int sqrt(int x). Compute and return the square root of x. Example sqrt ...

  4. Mat::operator =

    Provides matrix assignment operators. C++: Mat& Mat::operator=(const Mat& m) C++: Mat& M ...

  5. SQL中使用视图的优点和缺点是什么

    视图的优点与缺点 在程序设计的时候必须先了解视图的优缺点,这样可以扬长避短,视图具有如下的一些优点: ● 简单性.视图不仅可以简化用户对数据的理解,也可以简化他们的操作.那些被经常使用的查询可以被定义 ...

  6. hdu3208 Power of Integer

    /** 题目:H - Power of Integer 链接:https://vjudge.net/contest/152887#problem/H 题意:给出区间[a, b],问你区间[a, b]所 ...

  7. spark单机模式

    1.下载spark,解压2.复制conf/spark-env.sh和conf/log4j.properties cp spark-env.sh.template spark-env.sh cp log ...

  8. 18 已知下面的字符串是通过RANDOM随机数变量md5sum|cut-c 1-8截取后的结果

    面试题18:破解RANDOM随机数案例 已知下面的字符串是通过RANDOM随机数变量md5sum后,再截取一部分连续字符串的结果,请破解这些字符串对应的使用md5sum处理前的RANDOM对应的数字? ...

  9. Cocoapod安装使用和常见问题

    1.cocoapod的按照,cocoapod是运行在ruby环境下的,在ruby环境的 ,像cocoapod这样的开源项目时放在放在rubygems服务器上面的,但国内访问https://rubyge ...

  10. go语言调用cmd

    package main import ( "fmt" "os/exec" ) func main() { //删除C:\Users\Administrator ...