分层架构下的纯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. packagecom.tjitcast.common;
  2. importjava.io.IOException;
  3. importjava.sql.Connection;
  4. importjava.sql.SQLException;
  5. importjava.util.Properties;
  6. importjavax.sql.DataSource;
  7. importcom.mchange.v2.c3p0.DataSources;
  8. importcom.tjitcast.dao.DaoException;
  9. /**
  10. *数据库工具类
  11. *可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上
  12. *可以获取JDBC的事务管理器
  13. *@authorqiujy
  14. *@version0.9Beta
  15. */
  16. publicclassDbUtils{
  17. privatestaticPropertiesprop=newProperties();
  18. /**数据源*/
  19. privatestaticDataSourceds=null;
  20. //用来把Connection绑定到当前线程上的变量
  21. privatestaticThreadLocal<Connection>tl=newThreadLocal<Connection>();
  22. static{
  23. try{
  24. prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));
  25. }catch(IOExceptione){
  26. e.printStackTrace();
  27. System.out.println("在classpath下没有找到jdbc.properties文件");
  28. }
  29. //使用C3P0连接池技术
  30. try{
  31. Class.forName("com.mysql.jdbc.Driver");
  32. DataSourceunpooled=DataSources.unpooledDataSource(
  33. prop.getProperty("url"),
  34. prop.getProperty("user"),
  35. prop.getProperty("password"));
  36. ds=DataSources.pooledDataSource(unpooled);
  37. }catch(ClassNotFoundExceptione){
  38. e.printStackTrace();
  39. }catch(SQLExceptione){
  40. e.printStackTrace();
  41. }
  42. }
  43. privateDbUtils(){}
  44. /**
  45. *根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上
  46. *@return成功,返回Connection对象,否则返回null
  47. */
  48. publicstaticsynchronizedConnectiongetConnection(){
  49. Connectionconn=tl.get();//先从当前线程上取出连接实例
  50. if(null==conn){//如果当前线程上没有Connection的实例
  51. try{
  52. conn=ds.getConnection();//从连接池中取出一个连接实例
  53. tl.set(conn);//把它绑定到当前线程上
  54. }catch(SQLExceptione){
  55. e.printStackTrace();
  56. }
  57. }
  58. returnconn;
  59. }
  60. /**
  61. *获取事务管理器
  62. *@return事务管理实例
  63. */
  64. publicstaticsynchronizedTransactionManagergetTranManager(){
  65. returnnewTransactionManager(getConnection());
  66. }
  67. /**
  68. *关闭数据库连接,并卸装线程绑定
  69. *@paramconn要关闭数据库连接实例
  70. *@throwsDaoException
  71. */
  72. protectedstaticvoidclose(Connectionconn)throwsDaoException{
  73. if(conn!=null){
  74. try{
  75. conn.close();
  76. }catch(SQLExceptione){
  77. thrownewDaoException("关闭连接时出现异常",e);
  78. }finally{
  79. tl.remove();//卸装线程绑定
  80. }
  81. }
  82. }
  83. }

如下事务管理器类:

  1. packagecom.tjitcast.common;
  2. importjava.sql.Connection;
  3. importjava.sql.SQLException;
  4. importcom.tjitcast.dao.DaoException;
  5. /**
  6. *事务管理器
  7. *@authorqiujy
  8. *@version0.9Beta
  9. */
  10. publicclassTransactionManager{
  11. privateConnectionconn;
  12. protectedTransactionManager(Connectionconn){
  13. this.conn=conn;
  14. }
  15. /**开启事务*/
  16. publicvoidbeginTransaction()throwsDaoException{
  17. try{
  18. conn.setAutoCommit(false);//把事务提交方式改为手工提交
  19. }catch(SQLExceptione){
  20. thrownewDaoException("开户事务时出现异常",e);
  21. }
  22. }
  23. /**提交事务并关闭连接*/
  24. publicvoidcommitAndClose()throwsDaoException{
  25. try{
  26. conn.commit();//提交事务
  27. }catch(SQLExceptione){
  28. thrownewDaoException("提交事务时出现异常",e);
  29. }finally{
  30. DbUtils.close(conn);
  31. }
  32. }
  33. /**回滚并关闭连接*/
  34. publicvoidrollbackAndClose()throwsDaoException{
  35. try{
  36. conn.rollback();
  37. }catch(SQLExceptione){
  38. thrownewDaoException("回滚事务时出现异常",e);
  39. }finally{
  40. DbUtils.close(conn);
  41. }
  42. }
  43. }

如下业务层类:

  1. packagecom.tjitcast.service;
  2. importjava.util.List;
  3. importcom.tjitcast.common.DbUtils;
  4. importcom.tjitcast.common.TransactionManager;
  5. importcom.tjitcast.dao.DaoException;
  6. importcom.tjitcast.dao.DaoFactory;
  7. importcom.tjitcast.dao.DeptDao;
  8. importcom.tjitcast.dao.EmployeeDao;
  9. importcom.tjitcast.entity.Dept;
  10. importcom.tjitcast.entity.Employee;
  11. importcom.tjitcast.entity.PageModel;
  12. /**
  13. *业务层门面-->添加事务控制
  14. *@authorqiujy
  15. */
  16. publicclassServiceFacade{
  17. privateDeptDaodeptDao=DaoFactory.getInstance("deptDao",DeptDao.class);
  18. privateEmployeeDaoempDao=DaoFactory.getInstance("empDao",EmployeeDao.class);
  19. /**
  20. *新增部门
  21. *@paramdept
  22. */
  23. publicvoidinsertDept(Deptdept){
  24. TransactionManagertx=DbUtils.getTranManager();
  25. try{
  26. tx.beginTransaction();
  27. deptDao.insert(dept);
  28. tx.commitAndClose();
  29. }catch(DaoExceptione){
  30. tx.rollbackAndClose();
  31. }
  32. }
  33. /**
  34. *新增员工
  35. *@paramemp员工
  36. */
  37. publicvoidinsertEmp(Employeeemp){
  38. TransactionManagertx=DbUtils.getTranManager();
  39. try{
  40. tx.beginTransaction();
  41. empDao.insert(emp);
  42. tx.commitAndClose();
  43. }catch(DaoExceptione){
  44. tx.rollbackAndClose();
  45. }
  46. }
  47. /**
  48. *获取所有部门的列表
  49. *@return部门列表
  50. */
  51. publicList<Dept>getDeptList(){
  52. List<Dept>list=null;
  53. TransactionManagertx=DbUtils.getTranManager();
  54. try{
  55. tx.beginTransaction();
  56. list=deptDao.getDeptList();
  57. tx.commitAndClose();
  58. }catch(DaoExceptione){
  59. e.printStackTrace();
  60. tx.rollbackAndClose();
  61. }
  62. returnlist;
  63. }
  64. /**
  65. *获取指定部门下的员工分页列表
  66. *@paramdeptId
  67. *@parampageNo
  68. *@parampageSize
  69. *@return符合条件的PageModel
  70. */
  71. publicPageModel<Employee>getEmpListByDeptId(intdeptId,intpageNo,intpageSize){
  72. PageModel<Employee>pm=null;
  73. TransactionManagertx=DbUtils.getTranManager();
  74. try{
  75. tx.beginTransaction();
  76. pm=empDao.getEmpListByDeptId(deptId,pageNo,pageSize);
  77. tx.commitAndClose();
  78. }catch(DaoExceptione){
  79. tx.rollbackAndClose();
  80. }
  81. returnpm;
  82. }
  83. /**
  84. *删除指定ID的部门
  85. *@paramid部门ID
  86. */
  87. publicvoiddeleteDept(intid){
  88. TransactionManagertx=DbUtils.getTranManager();
  89. try{
  90. tx.beginTransaction();
  91. empDao.deleteByDeptId(id);//先删除指定ID部门下的所有员工
  92. deptDao.delete(id);//再删除该部门
  93. tx.commitAndClose();
  94. }catch(DaoExceptione){
  95. tx.rollbackAndClose();
  96. }
  97. }
  98. }

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

原文 :

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值