0%

Spring 事务

Transaction

  • JDBC事务
  • Spring 声明式事务
  • Spring 编程式事务
  • Spring 声明式事务-事务失效

JDBC Connection事务

  • setAutoCommit(false)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
    // so we don't want to do it unnecessarily (for example if we've explicitly
    // configured the connection pool to set it already).
    if (con.getAutoCommit()) {
    txObject.setMustRestoreAutoCommit(true);
    if (logger.isDebugEnabled()) {
    logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
    }
    con.setAutoCommit(false);
    }
  • execute SQL…

  • Commit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    @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 translateException("JDBC commit", 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 translateException("JDBC rollback", ex);
    }
    }

Spring 声明式事务

其实就是将事务代码和业务代码隔离开发, 然后通过一段配置让他们组装运行, 最后达到事务控制的目的.

声明式事务就是通过AOP原理实现的.

优势

  • 非侵入式开发,Spring推荐的开发方式,保证业务代码不受污染,只要加上@Transactional(rollbackFor = Exception.class)即可

不足

  • 最细粒度只能作用到方法级别。但可以通过抽取事务代码为独立代码块进行处理

原理

@EnableTransactionManagement

利用TransactionManagementConfigurationSelector给容器中导入两个组件:

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration
AutoProxyRegistrar
  • 给容器中注册一个InfrastructureAdvisorAutoProxyCreator后置处理器组件
  • InfrastructureAdvisorAutoProxyCreator 利用后置处理器机制在对象创建后,包装一个代理类对象返回,代理对象执行方法利用拦截器进行调用
ProxyTransactionManagementConfiguration
  • 给容器中注册事务增强器事务增强器要用事务的注解信息
  • AnnotationTransactionAttributeSource解析事务注解
事务拦截器
  • TransactionInterceptor,保存了事务属性信息,事务管理器
    这是一个MethodInterceptor;
    在目标方法执行的时候:执行拦截器链(事务拦截器)
    • 先获取事务相关的属性
    • 再获取PlatformTransactionManager,如果事先没有添加指定任何TransactionManager,最终会从容器中按照类型获取一个PlatformTransactionManager;
    • 执行目标方法
      如果异常,获取到事务管理器,利用事务管理回滚操作,
      如果正常,利用事务管理器,提交事务

Spring 编程式事务

将业务代码和事务代码放在一起书写,耦合性太高,不推荐使用

Transaction Template

模版方法,经常Template + Callback配合使用

  • 事务获取

  • //org.springframework.transaction.support.TransactionTemplate#execute
      try {
        result = action.doInTransaction(status);
      }
    
    1
    2
    3
    4
    5
    6
    7
    8

    - connection此时是打开的,此时尽量不要做耗时操作,如调用外域(Dubbo)、读写本地文件 等

    - 多次注册事务形为

    ```java
    methodA() | insert |
    methodB() | insert |

事务同步器 TransactionSynchronizationManager

  • 事务结束,后置处理,可以做成 同步/异步 方法调用,建议做成异步(如ThreadLocal)调用
  • 持久化
  • 后置处理如果是 同步状态,使用TransactionSynchronizationManager再开事务不会再执行,不建议事务结束后再开事务

Spring 声明式事务失效

访问权限问题

例如:private修饰的方法,Spring要求被代理的方法必须是public的

方法用final修饰

方法内部调用

同一个类中a方法调用有事务注解的b方法

Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用(相当于new Bean.b(),此时不受代理类控制),所以注解会失效。

解决方法

  • 1)将B方法移到另一个service中
  • 2)在service中注入自己
  • 3)通过aopContent类:在该service类中调用AopContent.currentProxy ()获取代理对象

解决原理

相当于b方法变为代理对象的方法,相当于代理对象调用,不会因new Bean导致事务失效

类未被spring管理

多线程调用

表不支持事务

未开启事务

Source

https://www.bilibili.com/video/BV1De4y1M7kL/