数据库事务(二): java事务处理

在平时我们做业务开发的时候,数据库操作一般都需要使用到事务。如果是基于spring的项目,很简单,有两种方法:
1.配置一个DataSource给spring容器托管,再配置一个TransactionManager事务管理器,然后在需要事务的方法上加上Transactional注解就ok,这样的话方法结束后事务才提交。
2. 配置好TransactionManager事务管理器后,可以在配置一个编程式事务模版,数据库操作时直接调用编程式事务就ok,而这样的话执行完所有sql语句后事务就提交,无需等待整个方法跑完。
这些简单操作的背后,框架给我们做了太多的工作,作为一个有技术追求的程序员,应该了解Java事务的底层工作原理。

JDBC提供的事务处理api

java通过jdbc与数据库进行交互,现在一般不直接使用jdbc,大多采用mybatis、hibernate等orm框架。但这些框架的底层,还是绕不开jdbc,事务处理同样如此。
JDBC提供的事务处理API非常少,请不要被Spring中事务处理的那一堆源代码所打击得信心尽失,这些框架提供的事务处理功能归根结底主要通过以java.sql.Connection类的方法完成:
Connection.setAutoCommit(boolean);
Connection.commit();
Connection.rollback();
在javax.sql.DataSource中,通过我们自己配置的数据库DataSource,可以获得Connection对象:

public interface DataSource  extends CommonDataSource, Wrapper {
    Connection getConnection() throws SQLException;
    ...
}

java底层的事务处理就是依靠上诉的两个对象和几个方法实现的。

举一个例子,银行转账方法transfer,将A账号下的一笔前转到B账号下,分两步,从A中扣除一笔钱,再在B中扣除一笔钱,代码如下:

public class BankServiceImpl implements BankService{
    private DataSource dataSource;
    //构造函数,传如DataSource对象
    public BankServiceImpl(DataSource dataSource){
        this.dataSource=dataSource;
    }
    //转账方法
    public void transfer(int fromId, int toId, int amount){
        Connection connection = dataSource.getConnection();
            //自动提交设为false
            connection.setAutoCommit(false);
            //调用仓储层方法扣款和加款
            bankDao.withdraw(fromId,amount);
            bankDao.deposit(toId,amount); 
            //提交
            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();  
            }  
        }  
    }
}

在上诉transfer方法中,要想保证数据的一致性,也就是事务起作用,必须保证整个转账流程都使用的是同一个Connection对象。也就是说,BankService.transfer()方法、BankDao.withdraw()、BankDao.deposit()方法,需调用同一个Connection对象。

因此,上诉的事务处理是错误的,无法保证Service和Dao层调用的是同一个Connection对象。
实际上,java事务处理,包括其它的一些框架spring,最关键的地方其实也是如何保证调用同一个Connection对象。
下一篇我们探讨如何自己构建一个TransactionManager,保证获得同一个Connection对象,从而实现可靠的事务处理,保证数据一致性。

你可能感兴趣的:(数据库)