SPRING TRANSACTIONAL-事务理解和使用



SPRING transactional 简介

事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性。
事务就是一系列的动作,它们被当作一个单独的工作单元,这些动作要么全部完成,要么全部不起作用.

事务的四个关键属性(ACID)

① 原子性(atomicity):事务是一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用.
② 一致性(consistency):一旦所有事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态中.
③ 隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据损坏.
④ 持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储器中.

事务回滚的触发机制

1 @transaction注解说明

        This annotation type is generally directly comparable to Spring's
        {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
        class, and in fact {@link AnnotationTransactionAttributeSource} will directly
        convert the data to the latter class, so that Spring's transaction support code
        does not have to know about annotations. If no rules are relevant to the exception,
        it will be treated like
        **{@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
        (rolling back on {@link RuntimeException} and {@link Error} but not on checked
        exceptions)**.

星号包裹部分明确指出:事务默认会在runtimeException 和 error 回滚,但是checked exception 不会回滚.

2 Checked exception 和 Unchecked exception

1.CheckedException已检查异常

CheckedException继承自Exception,程序内部无法控制,必须做处理,要么使用try-atch块捕获,要么throws抛给上一层。
正确的程序在运行中,很容易出现的、情理可容的异常状况。可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。

除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,
也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。

2.UnCheckedException未检查异常

UnCheckedException继承自RuntimeException,一般是程序的逻辑问题引起的异常,不需要捕获,也不需要处理, 比如空指针异常等,包括运行时异常(RuntimeException与其子类)和错误(Error)。

运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕>获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

3 异常的继承结构

当方法抛出runtimeException 或者error时 事务会回滚.

4 如何在事务回滚前trycatch异常,处理后再回滚,可以尝试下面的这三种方式

       SessionFactory sessionFactory = hibernateTemplate.getSessionFactory();
            Session session = sessionFactory.openSession();
            Transaction tx  = session.beginTransaction(); //开启事务
            try {
                //do something
            } catch (Exception e) {
                //do something 比如记录日志
                //第一种方式 
                tx.rollback();//手动回滚事务
                //第二种方式
                throw new RuntimeException("runtime");//重新抛出触发事务回滚机制的异常类 RuntimeException 或者 error
          //第三种方式
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }

5 @Transactional 注解添加到类级上的使用

当把@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息,EmployeeService 的所有方法都支持事务并且是只读。

当类级别配置了@Transactional,方法级别也配置了@Transactional时,应用程序会以方法级别的事务属性信息来管理事务。

      @Transactional(propagation= Propagation.SUPPORTS,readOnly=true)
      public class Test(){

        @Transactional(propagation= Propagation.REQUIRED,readOnly=false)//方法级别的事务属性信息会覆盖类级别的相关配置信息。
        public void insert(){

        }
        @Transactional(propagation= Propagation.REQUIRED,readOnly=true)//
        public void delete(){

        }
      }

6 Spring 的注解方式的事务实现机制

在应用系统调用声明@Transactional 的目标方法时,Spring Framework默认使用AOP代理,在代码运行时生成一个代理对象,根据@Transactional的属性配置信息,这个代理对象决定该声明@Transactional的目标方法是否由拦截器 TransactionInterceptor 来使用拦截。

在TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源DataSource提交或回滚事务.

Spring AOP 代理

Spring AOP 代理有CglibAopProxy和JdkDynamicAopProxy两种。

对于 CglibAopProxy,需要调用其内部类的DynamicAdvisedInterceptor的intercept方法。

对于 JdkDynamicAopProxy,需要调用其invoke方法。

正如上文提到的,事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager 的具体实现类来实现,如事务管理器 DataSourceTransactionManager。

不同的事务管理器管理不同的数据资源DataSource,比如 DataSourceTransactionManager管理JDBC的Connection。

transactional 的属性和使用

name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。

propagation 事务的传播行为,默认值为 REQUIRED。

isolation 事务的隔离度,默认值采用 DEFAULT。

timeout 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

rollback-for 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。

no-rollback- for 抛出no-rollback-for指定的异常类型,不回滚事务。

1 正确的设置@Transactional 的 propagation 属性

需要注意下面三种 propagation 可以不启动事务。本来期望目标方法进行事务管理,但若是错误的配置这三种 propagation,事务将不会发生回滚。

1 TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

2 TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

3 TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

2 正确的设置@Transactional 的 rollbackFor 属性

 @Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class)

默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者Error,则Spring将回滚事务;除此之外,Spring 不会回滚事务。

如果在事务中抛出其他类型的异常,并期望Spring能够回滚事务,可以指定rollbackFor。

通过分析 Spring 源码可以知道,若在目标方法中抛出的异常是rollbackFor指定的异常的子类,事务同样会回滚。

@Transactional注解应用到public方法,才能进行事务管理。

这是因为在使用 Spring AOP代理时,Spring在调用TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy的内部类)的intercept方法或JdkDynamicAopProxy的invoke方法.

这会间接调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法获取@Transactional注解的事务属性配置属性信息。

这个方法会检查目标方法的修饰符是不是public,若不是public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理,则事务失效。

    protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
            // Don't allow no-public methods as required.
            if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
              return null;
            }
    }

AOP事务自调用问题

在 Spring的AOP代理下,只有目标方法由外部调用,目标方法才由Spring生成的代理对象来管理,不然会造成自调用问题。

同一类中的其他没有@Transactional注解的方法内部调用有@Transactional注解的方法,有@Transactional注解的方法的事务被忽略,不会发生回滚。

    @Service
    public class OrderService {
        private void insert() {
          insertOrder();//同一类,insertOrder方法由insert调用,insert没有事务注解,导致insertOrder注解失效
        }

        @Transactional
        public void insertOrder() {
            //do something
         }
    }

insertOrder尽管有@Transactional注解,但它被内部方法insert调用,事务被忽略,出现异常事务不会发生回滚。

解决办法

上面的两个问题@Transactional注解只应用到public方法和自调用问题,是由于使用 Spring AOP 代理造成的。

为解决这两个问题,使用AspectJ取代Spring AOP代理。

1 增加ASPECTJ.XML配置

添加ASPECTJ.XML配置

      <tx:annotation-driven mode="aspectj" />
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
      </bean>
      </bean
        class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf">
        <property name="transactionManager" ref="transactionManager" />
      </bean>

2 更改pom.xml文件

同时在Maven的pom.xml文件中加入spring-aspects和aspectjrt的dependency以及aspectj-maven-plugin。

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.9</version>
    </dependency>
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.9</version>
    <configuration>
    <showWeaveInfo>true</showWeaveInfo>
    <aspectLibraries>
      <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
      </aspectLibrary>
    </aspectLibraries>
    </configuration>
    <executions>
      <execution>
      <goals>
        <goal>compile</goal>
        <goal>test-compile</goal>
      </goals>
      </execution>
    </executions>
    </plugin>

总结

以上是关于spring事务的一些机制和使用,以及aop的一些説明.



   Reprint policy


《SPRING TRANSACTIONAL-事务理解和使用》 by jackromer is licensed under a Creative Commons Attribution 4.0 International License
 Previous
JAVA-MONGODB简单使用 JAVA-MONGODB简单使用
概述 本文主要介绍MongoDB和java简单使用MongoDB,MongoDB作为流行的 NOT ONLY SQL 数据库,和关系型数据库mysql,oracle等的ACID不同,非关系型数据库为分布式系统和大数据的应用提供了良好的支持
2019-08-27
Next 
NETTY-NIO NETTY-NIO
什么是netty Netty是一个NIO网络编程框架,快速开发高性能、高可靠性的网络服务器/客户端程序。 极大地简化了TCP和UDP等网络编程。是一个异步事件驱动的网络框架,快速、高性能。RPC(pigeon、dubbo、HSF)Hado
2019-08-27
  目录