Java事务处理全解析(三)—— 丑陋的案例

xiaoxiao2021-02-28  115

在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,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 ? public class UglyBankDao  {      public void withdraw(int bankId, int amount, Connection connection) throws SQLException      {          PreparedStatement selectStatement = connection.prepareStatement(”SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?”);          selectStatement.setInt(1, bankId);          ResultSet resultSet = selectStatement.executeQuery();          resultSet.next();          int previousAmount = resultSet.getInt(1);          resultSet.close();          selectStatement.close();                     int newAmount = previousAmount - amount;          PreparedStatement updateStatement = connection.prepareStatement(”UPDATE BANK_ACCOUNT SET BANK_AMOUNT = ? WHERE BANK_ID = ?”);          updateStatement.setInt(1, newAmount);          updateStatement.setInt(2, bankId);          updateStatement.execute();                     updateStatement.close();      }  }   public class UglyBankDao { public void withdraw(int bankId, int amount, Connection connection) throws SQLException { PreparedStatement selectStatement = connection.prepareStatement("SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?"); selectStatement.setInt(1, bankId); ResultSet resultSet = selectStatement.executeQuery(); resultSet.next(); int previousAmount = resultSet.getInt(1); resultSet.close(); selectStatement.close(); int newAmount = previousAmount - amount; PreparedStatement updateStatement = connection.prepareStatement("UPDATE BANK_ACCOUNT SET BANK_AMOUNT = ? WHERE BANK_ID = ?"); updateStatement.setInt(1, newAmount); updateStatement.setInt(2, bankId); updateStatement.execute(); updateStatement.close(); } } 使用同样的方法,定义UglyInsuranceDao类:

[java] view plain copy print ? public class UglyInsuranceDao  {      public void deposit(int insuranceId, int amount, Connection connection) throws SQLException      {          PreparedStatement selectStatement = connection.prepareStatement(”SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?”);          selectStatement.setInt(1, insuranceId);          ResultSet resultSet = selectStatement.executeQuery();          resultSet.next();          int previousAmount = resultSet.getInt(1);          resultSet.close();          selectStatement.close();                              int newAmount = previousAmount + amount;          PreparedStatement updateStatement = connection.prepareStatement(”UPDATE INSURANCE_ACCOUNT SET INSURANCE_AMOUNT = ? WHERE INSURANCE_ID = ?”);          updateStatement.setInt(1, newAmount);          updateStatement.setInt(2, insuranceId);          updateStatement.execute();                    updateStatement.close();      }  }   public class UglyInsuranceDao { public void deposit(int insuranceId, int amount, Connection connection) throws SQLException { PreparedStatement selectStatement = connection.prepareStatement("SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?"); selectStatement.setInt(1, insuranceId); ResultSet resultSet = selectStatement.executeQuery(); resultSet.next(); int previousAmount = resultSet.getInt(1); resultSet.close(); selectStatement.close(); int newAmount = previousAmount + amount; PreparedStatement updateStatement = connection.prepareStatement("UPDATE INSURANCE_ACCOUNT SET INSURANCE_AMOUNT = ? WHERE INSURANCE_ID = ?"); updateStatement.setInt(1, newAmount); updateStatement.setInt(2, insuranceId); updateStatement.execute(); updateStatement.close(); } } 然后修改Service类,在UglyBankService类的transfer方法中,首先创建一个Connection对象,然后在将该对象依次传给UglyBankDao的withdraw方法和UglyInsuranceDao类的deposit方法,这样service层和DAO层使用相同的Connection对象。定义UglyBankService类如下:

[java] view plain copy print ? public class UglyBankService implements BankService  {      private DataSource dataSource;      private UglyBankDao uglyBankDao;      private UglyInsuranceDao uglyInsuranceDao;              public UglyBankService(DataSource dataSource)      {          this.dataSource = dataSource;      }              public void transfer(int fromId, int toId, int amount)      {          Connection connection = null;          try          {              connection = dataSource.getConnection();              connection.setAutoCommit(false);                      uglyBankDao.withdraw(fromId, amount, connection);              uglyInsuranceDao.deposit(toId, amount, connection);                      connection.commit();          } catch (Exception e)          {              try              {                  assert connection != null;                  connection.rollback();              } catch (SQLException e1)              {                  e1.printStackTrace();              }          } finally          {              try              {                  assert connection != null;                  connection.close();              } catch (SQLException e)              {                  e.printStackTrace();              }          }      }              public void setUglyBankDao(UglyBankDao uglyBankDao)      {          this.uglyBankDao = uglyBankDao;      }              public void setUglyInsuranceDao(UglyInsuranceDao uglyInsuranceDao)      {          this.uglyInsuranceDao = uglyInsuranceDao;      }  }   public class UglyBankService implements BankService { private DataSource dataSource; private UglyBankDao uglyBankDao; private UglyInsuranceDao uglyInsuranceDao; public UglyBankService(DataSource dataSource) { this.dataSource = dataSource; } public void transfer(int fromId, int toId, int amount) { Connection connection = null; try { connection = dataSource.getConnection(); connection.setAutoCommit(false); uglyBankDao.withdraw(fromId, amount, connection); uglyInsuranceDao.deposit(toId, amount, connection); connection.commit(); } catch (Exception e) { try { assert connection != null; connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { try { assert connection != null; connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } public void setUglyBankDao(UglyBankDao uglyBankDao) { this.uglyBankDao = uglyBankDao; } public void setUglyInsuranceDao(UglyInsuranceDao uglyInsuranceDao) { this.uglyInsuranceDao = uglyInsuranceDao; } }

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

 

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

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

转载请注明原文地址: https://www.6miu.com/read-69310.html

最新回复(0)