spring事物管理详解

最近再次拜读了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 源码深度解析-郝佳

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 1. 关于事务 事务控制就是将一系列操作当成一个不可拆分的逻辑单元,保证这些操作要么都成功,要么都失败。在关系数据...
    七弦桐语阅读 2,832评论 0 14
  • 这几天找工作,面试了将近10家公司,其中有几个问题几乎每家公司都会问,spring的事物机制应该算是其中最常被问到...
    我叫了了阅读 2,373评论 2 29
  • 事务的嵌套概念 所谓事务的嵌套就是两个事务方法之间相互调用。spring事务开启 ,或者是基于接口的或者是基于类的...
    jackcooper阅读 1,368评论 0 10
  • 事物的特性(ACID) 我们在使用JDBC或者Mybatis进行数据持久化操作时,我们的xml配置通常如下: 并发...
    Y了个J阅读 1,161评论 0 1
  • Life is so hard, right?
    喵喵诗茵阅读 403评论 0 1