Spring in @Transcation principle, Notes

First, the principle

spring Affairs implemented in two ways, jdk dynamic proxy implementation based on cglib achieve. spring based on the default jdk dynamic proxy, springboot after seemingly 2.x default is to use cglib, of course, choose which way you are configurable. The principle is simply Either way
dynamically generated proxy class runtime, load the class, after the proxy class method of execution enhanced.

  • jdk dynamic interface generation agent is based on the interface implementation class, interface method or private, protected method is not the agent can not be
  • cglib agent is based on the subclass inherits generates proxy class

Here Insert Picture Description

Second, simple to use

Contact Spring transaction management interface involved are as follows:
Here Insert Picture Description
we used mybatis using DataSourceTransactionManager class
programmatic matters much to say here, where they talk about annotation-based declarative transaction.
For example detailed configuration is not here , after configured to use very simple, usually coupled with @Transactinal to comment on the method.

    @Override
    @Transactional
    public int update() {
        userService.update2();
        return userMapper.update();
    }

Third, the rollback strategy

@Transcation default rollback only throw RuntimeException and Error unusual transaction , therefore default If you throw SQLException, IOException if not treated can not roll back the transaction.
What abnormal rollback can be specified by setting the rollbackFor way

//这样指定了,error也一样会回滚
@Transactional(rollbackFor = Exception.class)

error will be rolled back
three conclusions:

  1. When we throw an exception as its sub-categories or RunTime Error and its subclasses time. Regardless rollbackFor exception is valid, the transaction will be rolled back.
  2. 当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,必须根据rollbackfor进行回滚。比如rollbackfor=RuntimeException,而抛出IOException时候,事务是不进行回滚的。
  3. 当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,如果嵌套事务中,只要有一个rollbackfor允许回滚,则整个事务回滚。

注意下面这种方式,在使用spring整合mybatis的项目中,user表添加了唯一索引模拟sql异常,最后打印

====异常类型:class org.springframework.dao.DuplicateKeyException

SQL异常被spring封装成了Runtime异常,如果大家想测试上面的结论直接手动throw相应的异常就可以了

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            int insert2 = userMapper.insert(user);
            return insert;
        } catch (Exception e){
            System.out.println("====异常类型:"+e.getClass());
            throw e;
        }
    }

Here Insert Picture Description

四、嵌套事务的回滚策略

事务嵌套回滚策略由事务的传播行为决定,可以通过如下方式指定:

    @Transactional(propagation = Propagation.REQUIRED)
事务传播行为类型 说明 外围方法不开启事务 外围方法开启事务
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。外围方法不会加入到任一事务 外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 - -
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 - -
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 同第一排PROPAGATION_REQUIRED 开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 - -
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 - -
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 同第一排PROPAGATION_REQUIRED 外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务

五、事务方法调用自己类下面的其它方法

如下:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            this.update();//调用本类中其它添加了事务注解的方法,导致update事务不生效
            return insert;
        } catch (Exception e){
            throw new RuntimeException();
        }
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int update() {
        try {
            return userMapper.update();
        } catch (Exception e){
            throw new RuntimeException();
        }
    }

update事务失效是因为调用的动态代理类的增强方法,该方法内调用实际被增强类的方法,方法内this.xxx();,并不是使用的代理类。
Here Insert Picture Description
解决方法:
使用增强后的代理类调用该方法,使用@Autowired或从spring容器获取bean,如果该Service加了事务注解,拿到的bean类型其实就是$ProxyXX等代理类并不是我们自己写的Service
如果使用的是springBoot,在启动类添加:@EnableAspectJAutoProxy(ExposeProxy=true) 在通过
AopContext.currentProxy()可以拿到当前类的代理类;

    @Autowired
    private UserService userService;
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            //this.update();//调用本类中其它添加了事务注解的方法,导致update事务不生效
            userService.update();//替换为调用增强类的方法
            return insert;
        } catch (Exception e){
            throw new RuntimeException();
        }
    }

六、事务注解的timeout

Generally, we use the default value, the default is -1 wait for the results of the transaction will never timeout.
You can refer to another blog: spring transaction timeout test
spring Affairs transaction timeout = sql finished last time - the start time of the transaction.

Published 52 original articles · won praise 7 · views 3792

Guess you like

Origin blog.csdn.net/maomaoqiukqq/article/details/103941157