java事务全解析(三)--丑陋的案例

来源:互联网 发布:淘宝手机端买家秀 编辑:程序博客网 时间:2024/04/30 08:33

在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,service层和各个DAO所使用的Connection是不一样的,而JDBC中事务处理的作用对象正是Connection对象,所以不同DAO中的操作不在同一个事务里面,从而导致事务失败。从中我们得出了教训:要避免这种失败,我们可以使所有操作共享一个Connection对象,这样应该就没有问题了。

 

请通过以下方式下载本系列文章的github源代码:

Git clone https://github.com/davenkin/java_transaction_workshop.git

 

在本篇文章中,我们将看到一个成功的,但是丑陋的事务处理方案,它的基本思路是:在service层创建Connection对象,再将该Connection传给各个DAO类,这样就完成了Connection共享的目的。

 

修改两个DAO类,使他们都接受一个Connection对象,定义UglyBankDao类如下:

[java] view plain copy
print?
  1. public class UglyBankDao  
  2. {  
  3.     public void withdraw(int bankId, int amount, Connection connection) throws SQLException  
  4.     {  
  5.         PreparedStatement selectStatement = connection.prepareStatement("SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?");  
  6.         selectStatement.setInt(1, bankId);  
  7.         ResultSet resultSet = selectStatement.executeQuery();  
  8.         resultSet.next();  
  9.         int previousAmount = resultSet.getInt(1);  
  10.         resultSet.close();  
  11.         selectStatement.close();  
  12.            
  13.         int newAmount = previousAmount - amount;  
  14.         PreparedStatement updateStatement = connection.prepareStatement("UPDATE BANK_ACCOUNT SET BANK_AMOUNT = ? WHERE BANK_ID = ?");  
  15.         updateStatement.setInt(1, newAmount);  
  16.         updateStatement.setInt(2, bankId);  
  17.         updateStatement.execute();  
  18.            
  19.         updateStatement.close();  
  20.     }  
  21. }  

使用同样的方法,定义UglyInsuranceDao类:

[java] view plain copy
print?
  1. public class UglyInsuranceDao  
  2. {  
  3.     public void deposit(int insuranceId, int amount, Connection connection) throws SQLException  
  4.     {  
  5.         PreparedStatement selectStatement = connection.prepareStatement("SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?");  
  6.         selectStatement.setInt(1, insuranceId);  
  7.         ResultSet resultSet = selectStatement.executeQuery();  
  8.         resultSet.next();  
  9.         int previousAmount = resultSet.getInt(1);  
  10.         resultSet.close();  
  11.         selectStatement.close();  
  12.           
  13.           
  14.         int newAmount = previousAmount + amount;  
  15.         PreparedStatement updateStatement = connection.prepareStatement("UPDATE INSURANCE_ACCOUNT SET INSURANCE_AMOUNT = ? WHERE INSURANCE_ID = ?");  
  16.         updateStatement.setInt(1, newAmount);  
  17.         updateStatement.setInt(2, insuranceId);  
  18.         updateStatement.execute();  
  19.           
  20.         updateStatement.close();  
  21.     }  
  22. }  

然后修改Service类,在UglyBankService类的transfer方法中,首先创建一个Connection对象,然后在将该对象依次传给UglyBankDao的withdraw方法和UglyInsuranceDao类的deposit方法,这样service层和DAO层使用相同的Connection对象。定义UglyBankService类如下:

[java] view plain copy
print?
  1. public class UglyBankService implements BankService  
  2. {  
  3.     private DataSource dataSource;  
  4.     private UglyBankDao uglyBankDao;  
  5.     private UglyInsuranceDao uglyInsuranceDao;  
  6.         
  7.     public UglyBankService(DataSource dataSource)  
  8.     {  
  9.         this.dataSource = dataSource;  
  10.     }  
  11.         
  12.     public void transfer(int fromId, int toId, int amount)  
  13.     {  
  14.         Connection connection = null;  
  15.         try  
  16.         {  
  17.             connection = dataSource.getConnection();  
  18.             connection.setAutoCommit(false);  
  19.         
  20.             uglyBankDao.withdraw(fromId, amount, connection);  
  21.             uglyInsuranceDao.deposit(toId, amount, connection);  
  22.         
  23.             connection.commit();  
  24.         } catch (Exception e)  
  25.         {  
  26.             try  
  27.             {  
  28.                 assert connection != null;  
  29.                 connection.rollback();  
  30.             } catch (SQLException e1)  
  31.             {  
  32.                 e1.printStackTrace();  
  33.             }  
  34.         } finally  
  35.         {  
  36.             try  
  37.             {  
  38.                 assert connection != null;  
  39.                 connection.close();  
  40.             } catch (SQLException e)  
  41.             {  
  42.                 e.printStackTrace();  
  43.             }  
  44.         }  
  45.     }  
  46.         
  47.     public void setUglyBankDao(UglyBankDao uglyBankDao)  
  48.     {  
  49.         this.uglyBankDao = uglyBankDao;  
  50.     }  
  51.         
  52.     public void setUglyInsuranceDao(UglyInsuranceDao uglyInsuranceDao)  
  53.     {  
  54.         this.uglyInsuranceDao = uglyInsuranceDao;  
  55.     }  
  56. }  

通过上面共享Connection对象的方法虽然可以完成事务处理的目的,但是这样做法是丑陋的,原因在于:为了完成事务处理的目的,我们需要将一个底层的Connection类在service层和DAO层之间进行传递,而DAO层的方法也要接受这个Connection对象,这种做法显然是不好的,这就是典型的API污染。

 

在下一篇博文中,我们将讲到如何在不传递Connection对象的情况下完成和本文相同的事务处理功能。


转载地址:http://www.davenkin.me/post/2013-02-22/40049367747