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


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

     

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

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

    复制代码
    package davenkin.step2_ugly;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    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类:

    复制代码
    package davenkin.step2_ugly;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    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类如下:

    复制代码
    package davenkin.step2_ugly;
    
    import davenkin.BankService;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    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对象的情况下完成和本文相同的事务处理功能。

     
     
  • 相关阅读:
    【CSS】flex 布局 justifycontent:spacebetween;解决最后一排数量不够自动向两端排列问题
    【Vue】NavBar 顶部弹窗点击弹窗击空白区域关闭弹窗实现
    【patchpackage】基本使用,给修改的 node_modules 下源码“打补丁”
    【FSResizer】faststone下图片神器
    【pandas】按照数据列中元素出现的先后顺序进行分组排列(最后一种个人原创)
    【PC】微信多开bat
    【正则表达式应用】不规则多维数组展平一维
    【首发】【Navicat premium 16】激活完整记录
    【原创】彻底解决GitHub访问慢的问题(只需三招)
    【Edge浏览器】8大隐藏功能,开启更好用
  • 原文地址:https://www.cnblogs.com/fengjian/p/4209655.html
Copyright © 2020-2023  润新知