spring事物管理详解

作者: 默写流年 | 来源:发表于2019-07-17 16:00 被阅读76次

    最近再次拜读了spring的源码,对关于spring事物的应用部分加以整理,做如下笔记。如有不正确的地方,欢迎指正,谢谢。本文大致分为如下板块:

    1. 数据库事物
      1.1 ACID
    2. spring事物管理
      2.1 PlatformTransactionManager
      2.2 @Transactional
    3. 事物管理器
      3.1 jdbc事物管理器
      3.2 hibernate事物管理器
      3.3 其它事物管理器
    4. spring 事物的基本行为
      4.1 事物的传播行为
      4.1.1 PROPAGATION_REQUIRED
      4.1.2 PROPAGATION_SUPPORTS
      4.1.3 PROPAGATION_MANDATORY
      4.1.4 PROPAGATION_REQUIRES_NEW
      4.1.5 PROPAGATION_NOT_SUPPORTED
      4.1.6 PROPAGATION_NEVER
      4.1.7 PROPAGATION_NESTED
      4.2 事物的隔离级别
      4.2.1 数据库的隔离问题及级别
      4.2.1.1 隔离问题
      4.2.1.1 隔离级别
      4.2.2 Spirng 的事物隔离级别-Isolation
      4.3 事物的只读属性-readOnly
      4.4 事物的超时时间-timeout
      4.5 回滚规则-rollbackFor
      4.6 其它属性
      总结

    1.数据库事物

    数据库事物,指的是数据库执行的逻辑单元,这个逻辑单元遵从ACID原则,用这个原则来保证数据的一致性和完整性。反应到数据库上就是一组sql,要么全部执行成功,要么全部执行失败。例如下面sql:

    --uid001账户余额增加100块
    update user_account set account_balance=account_balance+100 where user_id='uid001';
    --uid002账户余额减少100块
    update user_account set account_balance=account_balance-100 where user_id='uid002';

    1.1 ACID

    特性 英文 说明
    原子性 Atomicity 事物的逻辑单元由一系列操作组成,这一系列操作要么全部成功,要么全部失败,回滚到未执行前一刻的状态
    一致性 Consistency 无论操作结果是成功还是失败,数据的业务状态是保持一致的。如uid001给uid002转账,无论成功还是失败,最终uid001和uid002的账户总和是一致的。
    隔离性 Isolation 数据库同时会存在多个事物,多个事物之间是隔离的,一个事物的操作不会影响到另一个事物的操作结果。但也不是完全隔离,数据库层面定义了多个隔离级别,不通隔离级别隔离的效果是不一样的,隔离的级别越高,数据一致性越好,但是并发度越弱。四个隔离级别由小到大如下:Read uncommitted<Read committed<Repeatable read<Serializable
    持久性 Durability 事物提交后,对数据的修改时永久的。及时系统发生故障,也能在不会丢失对数据的修改。一般通过执行前写入日志的方式保证持久性,即使系统崩溃,也能在重启的过程中,通过日志恢复崩溃时处于执行状态的事物。

    2.spring事物管理

    所谓的事物管理指的是“按照指定事物规则执行提交或者回滚操作”,而spring的事物管理,指的是spring对事物管理规则的抽象。具体来说就是spring不直接管理事物,而是为了不同的平台(如jdbc hibernate jpa 等)提供了不同的事物管理器 ,将具体的事物管理委托给相应的平台实现,spring并不关心具体事物管理的实现,从而为事物管理提供了通用编程模型

    2.1 PlatformTransactionManager

    上述通用抽象反应到代码层面就是org.springframework.jdbc.datasource.PlatformTransactionManager 类。

    /**
         * 根据不同的事物行为,获取当前事物,或者新建事物。
         * @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
         * @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
         */
        TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    
        /**
         * 就给定的事物状态提交事物。如果事物状态被标记为回滚,则回滚事物
         * @param status 当前事物对象
         */
        void commit(TransactionStatus status) throws TransactionException;
    
        /**
         * 回滚当前事物
         * @param status 当前事物对象
         */
        void rollback(TransactionStatus status) throws TransactionException;
    

    2.2 @Transactional

    spring 中使用事物分为两种,一种是声明式事物,一种是编程式事物。考虑到编程式事物基本很少使用,故下文以声明式事物来介绍spring的事物。而一般我们又比较常用@Transactional来实现事物声明。@Transactional 常用以下属性propagation,tionisolation,readOnly,rollbackFor,rollbackForClassName,noRollbackFor,noRollbackForClassName


    3 事物管理器

    spring 为不同的平台提供了不同的事物管理器。不同的事物管理器都继承自抽象类org.springframework.transaction.support.AbstractPlatformTransactionManager。该抽象类实现了org.springframework.jdbc.datasource.PlatformTransactionManager ,实现了事物管理器的骨架实现,通过继承该类,仅仅关心平台相关内容即可实现不同的事物管理器。

    3.1 jdbc事物管理器

     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
     </bean>
    

    jdbc 事物管理器是通过javax.sql.DataSource.getConnection() 获取javax.sql.Connection,通过javax.sql.Connection来进行事物的提交和回滚

        @Override
        protected void doCommit(DefaultTransactionStatus status) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
            Connection con = txObject.getConnectionHolder().getConnection();
            if (status.isDebug()) {
                logger.debug("Committing JDBC transaction on Connection [" + con + "]");
            }
            try {
                con.commit();
            }
            catch (SQLException ex) {
                throw new TransactionSystemException("Could not commit JDBC transaction", ex);
            }
        }
    
        @Override
        protected void doRollback(DefaultTransactionStatus status) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
            Connection con = txObject.getConnectionHolder().getConnection();
            if (status.isDebug()) {
                logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
            }
            try {
                con.rollback();
            }
            catch (SQLException ex) {
                throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
            }
        }
    

    3.2 hibernate事物管理器

        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    

    HibernateTransactionManager 是通过绑定org.hibernate.SessionFactory来实现的 ,通过sessionFactory来绑定org.hibernate.Transaction,提交或者回滚的时候调用org.hibernate.Transaction的commit或者rollback来操作

    3.3 其它spring事物管理器

    除了上面介绍的两个常用的事物管理器之外,spring还提供了其它的事物管理器。如 org.springframework.orm.jpa,org.springframework.transaction.jta.JtaTransactionManager,org.springframework.jms.connection.JmsTransactionManager,org.springframework.transaction.jta.WebLogicJtaTransactionManager等。感兴趣的可以自行查看,在idea中查看PlatformTransactionManager实现类如下

    image.png

    4 spring 事物的基本行为

    通过org.springframework.transaction.getTransaction(TransactionDefinition definition)

          /**
         * 根据不同的事物行为,获取当前事物,或者新建事物。
         * @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
         * @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
         */
        TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    

    可以得知,事物的相关属性就定义在java.sql.Connection.TransactionDefinition类中。那么我们看看该类中分别定义了什么

       /**
         * 获取传播行为
         */
        int getPropagationBehavior();
    
        /**
         * 获取隔离级别
         */
        int getIsolationLevel();
    
        /**
         * 获取超时时间
         */
        int getTimeout();
    
        /**
         * 获取只读属性
         */
        boolean isReadOnly();
    
        /**
         * 获取事物的名称 默认为classname+"."+methodName
         */
        String getName();
    

    4.1 事物的传播行为

    所谓事物的传播行为,指的是被事物标记的方法(注解或者xml声明),之间进行嵌套调用时事物的传播规则。拿jdbc事物管理器来说,就是共用同一个jdbc connection,还是新建connection,还是抛异常。这个规则就叫做事物的传播行为。spring定义了如下传播行为:

    传播行为 说明
    PROPAGATION_REQUIRED 如果存在事物,则加入当前事物。如果不存在事物,则新建事物。
    PROPAGATION_SUPPORTS 支持事物,如果不存在事物则以非事物的状态运行。
    PROPAGATION_MANDATORY 必须存在事物,不存在则抛异常。
    PROPAGATION_REQUIRES_NEW 无论当前是否存在事物,都新建事物。仅支持JtaTransactionManager作为事物管理器
    PROPAGATION_NOT_SUPPORTED 不支持事物,如当前存在事物则将当前事物挂起,以非事物的方式运行。仅支持JtaTransactionManager作为事物管理器
    PROPAGATION_NEVER 不支持事物,如当前存在事物则抛异常。
    PROPAGATION_NESTED 嵌套事物。如果当前不存在事物,则以PROPAGATION_REQUIRED的方式运行。如果存在事物则以嵌套事物的方式运行。仅支持DataSourceTransactionManager作为事物管理器和部分JTA事物管理器

    4.1.1 PROPAGATION_REQUIRED

    如果存在事物,则加入当前事物。如果不存在事物,则新建事物。默认传播行为。

      void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.REQUIRED)
        void methodB(){
            //doSomethingB()
        }
    

    methodB()被调用时不存在事物,则会新建一个事物。methodB 会以事物方式运行。相当于如下操作

    void methodA(){
            //doSomethingA()
            Connection connection=null;
            try{
                connection=getConnection();
                //doSomethingB()
                connection.commit();
            }catch (RuntimeException e){
                connection.rollback();
            }finally {
                connection.close();
            }
        }
    

    方法内调用之所以用类似 ((ServiceName)AopContext.currentProxy()).methodB()。是因为spring事物是基于aop实现,aop又是基于代理类实现。直接方法内调用是不会使用到增强类,也就不会调用到被代理类的增强方法

    4.1.2 PROPAGATION_SUPPORTS

    支持事物,如果不存在事物则以非事物的状态运行。

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.SUPPORTS)
        void methodB(){
            //doSomethingB()
        }
    

    调用methodA时,相当于如下:

    void methodA(){
            Connection connection=null;
            try{
                connection=getConnection();
                //doSomethingA()
                //doSomethingB()
                connection.commit();
            }catch (RuntimeException e){
                connection.rollback();
            }finally {
                connection.close();
            }
        }
    

    当单独调用methodB 时,methodB 以非事物的方式运行。

    4.1.3 PROPAGATION_MANDATORY

    必须存在事物,不存在则抛异常。

        @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.MANDATORY)
        void methodB(){
            //doSomethingB()
        }
    

    无论时调用methodA,还是单独调用methodB都会抛异常

    // No existing transaction found -> check propagation behavior to find out how to proceed.
            if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
                throw new IllegalTransactionStateException(
                        "No existing transaction found for transaction marked with propagation 'mandatory'");
            }
    

    4.1.4 PROPAGATION_REQUIRES_NEW

    无论当前是否存在事物,都新建事物。

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            ((ServiceName)AopContext.currentProxy()).methodB();
            //doSomethingA()
        }
    
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        void methodB(){
            //doSomethingB()
            //successOrNot
        }
    

    当methodA中的doSomethingA()发生异常回滚时,methodB可以正常提交。
    相当于如下:

    void methodA(){
            //当前连接
            Connection currentConnection=null;
            try{
                currentConnection=getConnection();
                //挂起当前事物
                suspend(currentConnection);
                Connection newConnection;
                try {
                    //开启新事物
                     newConnection=getConnection();
                    //doSomethingB()
                    newConnection.commit();
                }catch (Exception e){
                    newConnection.rollback(sa);
                }finally {
                    newConnection.close();
                }
                //恢复当前事物
                resume(currentConnection);
                currentConnection.commit();
            }catch (RuntimeException e){
                currentConnection.rollback();
            }finally {
                currentConnection.close();
            }
        }
    

    新建事物不受外部事物的影响,PROPAGATION_REQUIRES_NEW可以用在保证接口最近在读spring源码的时候,对spring事物又有了一点新的理解。遂加以整理如下笔记。如有不正确的地方,欢迎指正,谢谢。本文大致分为如下板块:

    • 什么是数据库事物
    • 什么是spring事物
    • spring事物核心接口
    • 不同的事物管理器
    • 事物的传播行为
    • 事物的隔离级别
    • 事物工具类

    1.什么是数据库事物

    数据库事物,指的是数据库执行的逻辑单元,这个逻辑单元遵从ACID原则,用这个原则来保证数据的一致性和完整性。反应到数据库上就是一组sql,要么全部执行成功,要么全部执行失败。例如下面sql:

    --uid001账户余额增加100块
    update user_account set account_balance=account_balance+100 where user_id='uid001';
    --uid002账户余额减少100块
    update user_account set account_balance=account_balance-100 where user_id='uid002';

    1.1 ACID

    特性 英文 说明
    原子性 Atomicity 事物的逻辑单元由一系列操作组成,这一系列操作要么全部成功,要么全部失败,回滚到未执行前一刻的状态。
    一致性 Consistency 无论操作结果是成功还是失败,数据的业务状态是保持一致的。如uid001给uid002转账,无论成功还是失败,最终uid001和uid002的账户总和是一致的。
    隔离性 Isolation 数据库同时会存在多个事物,多个事物之间是隔离的,一个事物的操作不会影响到另一个事物的操作结果。但也不是完全隔离,数据库层面定义了多个隔离级别,不通隔离级别隔离的效果是不一样的,隔离的级别越高,数据一致性越好,但是并发度越弱。四个隔离级别由小到大如下:Read uncommitted<Read committed<Repeatable read<Serializable
    持久性 Durability 事物提交后,对数据的修改时永久的。及时系统发生故障,也能在不会丢失对数据的修改。一般通过执行前写入日志的方式保证持久性,即使系统崩溃,也能在重启的过程中,通过日志恢复崩溃时处于执行状态的事物。

    2.什么是spring事物管理

    所谓的事物管理指的是“按照指定事物规则执行提交或者回滚操作”,而spring的事物管理,指的是spring对事物管理规则的抽象。具体来说就是spring不直接管理事物,而是为了不同的平台提供了不同的事物管理器 如jdbc hibernate jpa 等,将具体的事物管理委托给相应的持久化框架实现,spring并不关心具体事物管理的实现,具体的实现留给对应的平台来实现,从而为事物管理提供了通用编程模型。这个通用抽象反应到代码层面就是org.springframework.jdbc.datasource.PlatformTransactionManager 类。

    /**
         * 根据不同的事物行为,获取当前事物,或者新建事物。
         * @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
         * @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
         */
        TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    
        /**
         * 就给定的事物状态提交事物。如果事物状态被标记为回滚,则回滚事物
         * @param status 当前事物对象
         */
        void commit(TransactionStatus status) throws TransactionException;
    
        /**
         * 回滚当前事物
         * @param status 当前事物对象
         */
        void rollback(TransactionStatus status) throws TransactionException;
    

    3 事物管理器

    spring 为不同的平台提供了不同的事物管理器。不同的事物管理器都继承自抽象类org.springframework.transaction.support.AbstractPlatformTransactionManager。该抽象类实现了org.springframework.jdbc.datasource.PlatformTransactionManager ,实现了事物管理器的骨架实现,通过继承该类,仅仅关心平台相关内容即可实现不同的事物管理器。

    3.1 jdbc事物管理器

     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
     </bean>
    

    jdbc 事物管理器是通过javax.sql.DataSource.getConnection() 获取javax.sql.Connection,通过javax.sql.Connection来进行事物的提交和回滚。

        @Override
        protected void doCommit(DefaultTransactionStatus status) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
            Connection con = txObject.getConnectionHolder().getConnection();
            if (status.isDebug()) {
                logger.debug("Committing JDBC transaction on Connection [" + con + "]");
            }
            try {
                con.commit();
            }
            catch (SQLException ex) {
                throw new TransactionSystemException("Could not commit JDBC transaction", ex);
            }
        }
    
        @Override
        protected void doRollback(DefaultTransactionStatus status) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
            Connection con = txObject.getConnectionHolder().getConnection();
            if (status.isDebug()) {
                logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
            }
            try {
                con.rollback();
            }
            catch (SQLException ex) {
                throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
            }
        }
    

    3.2 hibernate事物管理器

        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    

    HibernateTransactionManager 是通过绑定org.hibernate.SessionFactory来实现的 ,通过sessionFactory来绑定org.hibernate.Transaction,提交或者回滚的时候调用org.hibernate.Transaction的commit或者rollback来操作。

    @Override
        protected void doCommit(DefaultTransactionStatus status) {
            HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
            if (status.isDebug()) {
                logger.debug("Committing Hibernate transaction on Session [" +
                        txObject.getSessionHolder().getSession() + "]");
            }
            try {
                txObject.getSessionHolder().getTransaction().commit();
            }
            catch (org.hibernate.TransactionException ex) {
                // assumably from commit call to the underlying JDBC connection
                throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
            }
            catch (HibernateException ex) {
                // assumably failed to flush changes to database
                throw convertHibernateAccessException(ex);
            }
        }
    

    3.3 其它spring事物管理器

    除了上面介绍的两个常用的事物管理器之外,spring还提供了其它的事物管理器。如 org.springframework.orm.jpa,org.springframework.transaction.jta.JtaTransactionManager,org.springframework.jms.connection.JmsTransactionManager,org.springframework.transaction.jta.WebLogicJtaTransactionManager等。感兴趣的可以自行查看,在idea中查看PlatformTransactionManager实现类如下

    image.png

    4 spring 事物的基本行为

    通过org.springframework.transaction.getTransaction(TransactionDefinition definition)

          /**
         * 根据不同的事物行为,获取当前事物,或者新建事物。
         * @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
         * @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
         */
        TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    

    可以得知,事物的相关属性就定义在java.sql.Connection.TransactionDefinition类中。那么我们看看该类中分别定义了什么

       /**
         * 获取传播行为
         */
        int getPropagationBehavior();
    
        /**
         * 获取隔离级别
         */
        int getIsolationLevel();
    
        /**
         * 获取超时时间
         */
        int getTimeout();
    
        /**
         * 获取只读属性
         */
        boolean isReadOnly();
    
        /**
         * 获取事物的名称 默认为classname+"."+methodName
         */
        String getName();
    

    4.1 事物的传播行为-propagation

    所谓事物的传播行为,指的是被事物标记的方法(注解或者xml声明),之间进行嵌套调用时事物的传播规则。拿jdbc事物管理器来说,就是共用同一个jdbc connection,还是新建connection,还是抛异常。这个规则就叫做事物的传播行为。spring定义了如下传播行为:

    传播行为 说明
    PROPAGATION_REQUIRED *如果存在事物,则加入当前事物。如果不存在事物,则新建事物 *
    PROPAGATION_SUPPORTS 支持事物,如果不存在事物则以非事物的状态运行
    PROPAGATION_MANDATORY 必须存在事物,不存在则抛异常
    PROPAGATION_REQUIRES_NEW 无论当前是否存在事物,都新建事物
    PROPAGATION_NOT_SUPPORTED 不支持事物,如当前存在事物则将当前事物挂起,以非事物的方式运行
    PROPAGATION_NEVER 不支持事物,如当前存在事物则抛异常
    PROPAGATION_NESTED 嵌套事物。如果当前不存在事物,则以PROPAGATION_REQUIRED的方式运行。如果存在事物则以嵌套事物的方式运行

    4.1.1 PROPAGATION_REQUIRED

    如果存在事物,则加入当前事物。如果不存在事物,则新建事物。默认传播行为。

      void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.REQUIRED)
        void methodB(){
            //doSomethingB()
        }
    

    methodB()被调用时不存在事物,则会新建一个事物。methodB 会以事物方式运行。相当于如下操作

    void methodA(){
            //doSomethingA()
            Connection connection=null;
            try{
                connection=getConnection();
                //doSomethingB()
                connection.commit();
            }catch (RuntimeException e){
                connection.rollback();
            }finally {
                connection.close();
            }
        }
    

    方法内调用之所以用类似 ((ServiceName)AopContext.currentProxy()).methodB()。是因为spring事物是基于aop实现,aop又是基于代理类实现。直接方法内调用是不会使用到增强类,也就不会调用到被代理类的增强方法。

    4.1.2 PROPAGATION_SUPPORTS

    支持事物,如果不存在事物则以非事物的状态运行。

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.SUPPORTS)
        void methodB(){
            //doSomethingB()
        }
    

    调用methodA时,相当于如下:

    void methodA(){
            Connection connection=null;
            try{
                connection=getConnection();
                //doSomethingA()
                //doSomethingB()
                connection.commit();
            }catch (RuntimeException e){
                connection.rollback();
            }finally {
                connection.close();
            }
        }
    

    当单独调用methodB 时,methodB 以非事物的方式运行。

    4.1.3 PROPAGATION_MANDATORY

    必须存在事物,不存在则抛异常。

       void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.MANDATORY)
        void methodB(){
            //doSomethingB()
        }
    

    无论时调用methodA,还是耽误调用methodB都会抛异常

    // No existing transaction found -> check propagation behavior to find out how to proceed.
            if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
                throw new IllegalTransactionStateException(
                        "No existing transaction found for transaction marked with propagation 'mandatory'");
            }
    

    4.1.4 PROPAGATION_REQUIRES_NEW

    无论当前是否存在事物,都新建事物。

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            ((ServiceName)AopContext.currentProxy()).methodB();
            //doSomethingA()
        }
    
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        void methodB(){
            //doSomethingB()
            //successOrNot
        }
    

    当methodA中的doSomethingA()发生异常回滚时,methodB可以正常提交。值得注意的是,PROPAGATION_REQUIRES_NEW 仅仅支持JtaTransactionManager作为事物管理器。

    /**
         * Create a new transaction, suspending the current transaction if one exists.
         * Analogous to the EJB transaction attribute of the same name.
         * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
         * on all transaction managers. This in particular applies to
         * {@link org.springframework.transaction.jta.JtaTransactionManager},
         * which requires the {@code javax.transaction.TransactionManager} to be
         * made available it to it (which is server-specific in standard Java EE).
         * <p>A {@code PROPAGATION_REQUIRES_NEW} scope always defines its own
         * transaction synchronizations. Existing synchronizations will be suspended
         * and resumed appropriately.
         * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
         */
        int PROPAGATION_REQUIRES_NEW = 3;
    

    相当于如下:

    void methodA(){
            //当前连接
            Connection currentConnection=null;
            try{
                currentConnection=getConnection();
                //挂起当前事物
                suspend(currentConnection);
                Connection newConnection;
                try {
                    //开启新事物
                     newConnection=getConnection();
                    //doSomethingB()
                    newConnection.commit();
                }catch (Exception e){
                    newConnection.rollback(sa);
                }finally {
                    newConnection.close();
                }
                //恢复当前事物
                resume(currentConnection);
                currentConnection.commit();
            }catch (RuntimeException e){
                currentConnection.rollback();
            }finally {
                currentConnection.close();
            }
        }
    

    4.1.5 PROPAGATION_NOT_SUPPORTED

    不支持事物,如果当前存在事物,则将当前事物挂起,已非事物的方式运行。同样,仅支持JtaTransactionManager做为事物管理器

    /**
         * Do not support a current transaction; rather always execute non-transactionally.
         * Analogous to the EJB transaction attribute of the same name.
         * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
         * on all transaction managers. This in particular applies to
         * {@link org.springframework.transaction.jta.JtaTransactionManager},
         * which requires the {@code javax.transaction.TransactionManager} to be
         * made available it to it (which is server-specific in standard Java EE).
         * <p>Note that transaction synchronization is <i>not</i> available within a
         * {@code PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations
         * will be suspended and resumed appropriately.
         * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
         */
        int PROPAGATION_NOT_SUPPORTED = 4;
    
    

    4.1.6 PROPAGATION_NEVER

    不支持事物,如果当前存在事物,则抛异常。

    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
                throw new IllegalTransactionStateException(
                        "Existing transaction found for transaction marked with propagation 'never'");
            }
    

    4.1.7 PROPAGATION_NESTED

    嵌套事物。如果当前存在事物,则以嵌套事物的方式运行如果不存在事物,则以PROPAGATION_REQUIRED的方式运行。

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            //doSomethingA()
            ((ServiceName)AopContext.currentProxy()).methodB();
        }
    
        @Transactional(propagation = Propagation.NESTED)
        void methodB(){
            //doSomethingB()
            //successOrNot
        }
    

    单独调用methodB 时,相当于 PROPAGATION_REQUIRED。

    void method(){
            Connection connection=null;
            try{
                connection=getConnection();
                //doSomethingB()
                connection.commit();
            }catch (RuntimeException e){
                connection.rollback();
            }finally {
                connection.close();
            }
        }
    

    当调用methodA时,则以嵌套事物的方式运行,methodB作为methodA的子事物,提交和回滚都会受methodA的事物的影响

    void methodA(){
            Connection currentConnection=null;
            Savepoint savepoint=null;
            try{
                currentConnection=getConnection();
                //doSomethingA();
                savepoint=currentConnection.setSavepoint(createSavePoint());
                try{
                    //doSomethingB();
                }catch (RuntimeException e){
                    currentConnection.rollback(savepoint);
                }finally {
                    currentConnection.close();
                }
                currentConnection.commit();
            }catch (RuntimeException e){
                currentConnection.rollback();
            }finally {
                currentConnection.close();
            }
        }
    

    值得注意的一点是,嵌套事物只支持DataSourceTransactionManager,或者部分JTA事物管理器。且要求jdbc 3.0驱动及以上,同时 jdk版本在1.4 以上

    /**
         * Execute within a nested transaction if a current transaction exists,
         * behave like {@link #PROPAGATION_REQUIRED} else. There is no analogous
         * feature in EJB.
         * <p><b>NOTE:</b> Actual creation of a nested transaction will only work on
         * specific transaction managers. Out of the box, this only applies to the JDBC
         * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
         * when working on a JDBC 3.0 driver. Some JTA providers might support
         * nested transactions as well.
         * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
         */
        int PROPAGATION_NESTED = 6;
    
     /**
         * Creates a savepoint with the given name in the current transaction
         * and returns the new <code>Savepoint</code> object that represents it.
         *
         * <p> if setSavepoint is invoked outside of an active transaction, a transaction will be started at this newly created
         *savepoint.
         *
         * @param name a <code>String</code> containing the name of the savepoint
         * @return the new <code>Savepoint</code> object
         * @exception SQLException if a database access error occurs,
              * this method is called while participating in a distributed transaction,
         * this method is called on a closed connection
         *            or this <code>Connection</code> object is currently in
         *            auto-commit mode
         * @exception SQLFeatureNotSupportedException if the JDBC driver does not support
         * this method
         * @see Savepoint
         * @since 1.4
         */
        Savepoint setSavepoint(String name) throws SQLException;
    

    一般我们用PROPAGATION_NESTED来执行分支逻辑

    @Transactional(propagation = Propagation.REQUIRED)
        void methodA(){
            try{
                ((ServiceName)AopContext.currentProxy()).methodB();
            }catch (Exception e){
                //doSomethingC();
            }
    
        }
    
        @Transactional(propagation = Propagation.NESTED)
        void methodB(){
            //doSomethingB()
            //successOrNot
        }
    

    当methodB执行失败的时候,则methodA 回滚到之前的保存点,然后执行catch块中的其它业务逻辑,就像methodB从未执行过一样。这也是PROPAGATION_NESTED最常用的用法,而PROPAGATION_REQUIRED和PROPAGATION_NEW都无法做到这一点。
    大多数情况下,PROPAGATION_REQUIRED可以满足我们的绝大部分需求,所以它也应该是我们优先考虑的传播行为,同时其也是@Transactional 的默认传播行为

    4.2 事物的隔离级别

    事物的隔离级别,指的是并发情况下,事物之间的隔离度。不同的隔离级别,可以解决不同的隔离问题,也对应着不同的并发度,使用的时候,要结合实际情况,按需选择。

    4.2.1 数据库的隔离问题及级别

    4.2.1.1 隔离问题

    问题 描述
    脏读 事物A读到了事物B未提交的数据,如果事物B的操作被回滚了,那么事物A读到的就是无效数据。
    不可重复读 事物A执行select 查询到结果集R1,此时事物B执行update 并提交,事物A再执行同样的select 查询到结果集R2。两次查询到的结果集不一致。
    幻读 事物A执行select 查询到结果集R1,此事事物B执行了 insert 或者 delete 操作 并提交,事物A再执行同样的select 查询到结果集R2。此时发现R2相比R1多了或者少了一些记录。

    4.2.1.2 隔离级别

    级别 描述 脏读 不可重复读 幻读
    Read Uncommitted 读未提交 × × ×
    Read Committed 读已提交,oracle 默认隔离级别,利用快照读解决脏读 × ×
    Repeated Read 重复读,mysql 默认隔离级别,除了读快照之外,事物启动后不允许执行update操作 ×
    Serializable 串行化,事物串行执行,效率最差,安全性也最高

    4.2.2 Spirng 的事物隔离级别-isolation

    spring的事物隔离级别也是依赖于底层的数据库隔离级别

    级别 描述
    ISOLATION_DEFAULT 使用数据库隔离级别。默认隔离级别
    ISOLATION_READ_UNCOMMITTED 读未提交
    ISOLATION_READ_COMMITTED 读已提交
    ISOLATION_REPEATABLE_READ 可重复读
    ISOLATION_SERIALIZABLE 串行化

    以DataSourceTransactionManager为例,是通过设置Connection的TransactionIsolation属性来实现绑定不同的隔离级别的。

    // Apply specific isolation level, if any.
            Integer previousIsolationLevel = null;
            if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
                            definition.getIsolationLevel());
                }
                int currentIsolation = con.getTransactionIsolation();
                if (currentIsolation != definition.getIsolationLevel()) {
                    previousIsolationLevel = currentIsolation;
                    con.setTransactionIsolation(definition.getIsolationLevel());
                }
            }
    

    4.3 事物的只读属性-readOnly

    spring 事物的只读属性是通过设置,java.sql.Connection 的 readOnly 属性来实现的。当设置了只读属性后,数据库会对只读事物进行一些优化,如不启用回滚段,不启用回滚日志等。同时值得注意的是,设置了只读属性后,如果进行写操作会报错。

    // Set read-only flag.
            if (definition != null && definition.isReadOnly()) {
                try {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Setting JDBC Connection [" + con + "] read-only");
                    }
                    con.setReadOnly(true);
                }
    

    4.4 事物的超时时间-timeout

    事物的超时指的是设置一个时间,当执行时间超过这个时间后,抛异常并回滚。是通过设置一个截至时间来实现的,值得注意的是,这个超时时间只有特定情况下才会生效,如dao层使用jdbcTemplete 执行sql

    设置超时时间

    int timeout = determineTimeout(definition);
                if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
                }
    /**
         * Set the timeout for this object in seconds.
         * @param seconds number of seconds until expiration
         */
        public void setTimeoutInSeconds(int seconds) {
            setTimeoutInMillis(seconds * 1000L);
        }
    
        /**
         * Set the timeout for this object in milliseconds.
         * @param millis number of milliseconds until expiration
         */
        public void setTimeoutInMillis(long millis) {
            this.deadline = new Date(System.currentTimeMillis() + millis);
        }
    

    检查超时时间

    /**
         * Return the time to live for this object in seconds.
         * Rounds up eagerly, e.g. 9.00001 still to 10.
         * @return number of seconds until expiration
         * @throws TransactionTimedOutException if the deadline has already been reached
         */
        public int getTimeToLiveInSeconds() {
            double diff = ((double) getTimeToLiveInMillis()) / 1000;
            int secs = (int) Math.ceil(diff);
            checkTransactionTimeout(secs <= 0);
            return secs;
        }
    /**
         * Set the transaction rollback-only if the deadline has been reached,
         * and throw a TransactionTimedOutException.
         */
        private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
            if (deadlineReached) {
                setRollbackOnly();
                throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
            }
        }
    

    4.5 回滚规则-rollbackFor

    回滚规则只得是spring 事物遇到哪种异常会回滚。默认只回滚RuntimeException和Error
    源码如下

    if (txInfo.transactionAttribute.rollbackOn(ex)) {
                    try {
                        txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                    }
                    catch (TransactionSystemException ex2) {
                        logger.error("Application exception overridden by rollback exception", ex);
                        ex2.initApplicationException(ex);
                        throw ex2;
                    }
                    catch (RuntimeException ex2) {
                        logger.error("Application exception overridden by rollback exception", ex);
                        throw ex2;
                    }
                    catch (Error err) {
                        logger.error("Application exception overridden by rollback error", ex);
                        throw err;
                    }
                }
    
    @Override
        public boolean rollbackOn(Throwable ex) {
            return (ex instanceof RuntimeException || ex instanceof Error);
        }
    

    当配置了rollbackFor 属性时,源码如下

    @Override
        public boolean rollbackOn(Throwable ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
            }
    
            RollbackRuleAttribute winner = null;
            int deepest = Integer.MAX_VALUE;
    
            if (this.rollbackRules != null) {
                for (RollbackRuleAttribute rule : this.rollbackRules) {
                    int depth = rule.getDepth(ex);
                    if (depth >= 0 && depth < deepest) {
                        deepest = depth;
                        winner = rule;
                    }
                }
            }
    
            if (logger.isTraceEnabled()) {
                logger.trace("Winning rollback rule is: " + winner);
            }
    
            // User superclass behavior (rollback on unchecked) if no rule matches.
            if (winner == null) {
                logger.trace("No relevant rollback rule found: applying default rules");
                return super.rollbackOn(ex);
            }
    
            return !(winner instanceof NoRollbackRuleAttribute);
        }
    
    public int getDepth(Throwable ex) {
            return getDepth(ex.getClass(), 0);
        }
    
    
        private int getDepth(Class<?> exceptionClass, int depth) {
            if (exceptionClass.getName().contains(this.exceptionName)) {
                // Found it!
                return depth;
            }
            // If we've gone as far as we can go and haven't found it...
            if (exceptionClass == Throwable.class) {
                return -1;
            }
            return getDepth(exceptionClass.getSuperclass(), depth + 1);
        }
    

    可以看到,此时回滚的规则为当前异常类型在配置的异常类型里面

    4.6 其它属性

    类似4.5的属性还有rollbackForClassName,noRollbackFor,noRollbackForClassName 。都是类似的作用,可以自行查看。

    总结

    任何场景下都应该根据合适的场景选择合适的方式,知其然还要只其所以然,才能在出先问题的时候,快读定位排查。后续我们将分析下spring的事物管理的源码,一起看下spring的事物究竟是怎么生效的。

    下一篇-spring源码系列-容器之XmlBeanFactory

    参考文章
    http://www.jianshu.com
    Spring 源码深度解析-郝佳

    相关文章

      网友评论

        本文标题:spring事物管理详解

        本文链接:https://www.haomeiwen.com/subject/xucxcctx.html