@Transtional

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

value:指定事务管理器

ropagation:事务传播行为

Isolation:事务隔离级别

timeout:事务超时时间,默认不超时(受连接池配置影响)

readOnly:是否只读

rollbackFor/rollbackForClassName:指定回滚异常

noRollbackFor/noRollbackForClassName:指定不回滚异常

propagation 事务传播行为枚举类:

public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);

private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}

//如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
@Transactional(propagation = Propagation.REQUIRED)
//创建一个新的事务,如果当前存在事务,则把当前事务挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
//如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
@Transactional(propagation = Propagation.SUPPORTS)
//以非事务方式运行,如果当前存在事务,则把当前事务挂起。
@Transactional(propagation = Propagation.NOT_SUPPORTED)
//如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
@Transactional(propagation = Propagation.MANDATORY)
//如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
@Transactional(propagation = Propagation.NESTED)
//以非事务方式运行,如果当前存在事务,则抛出异常。
@Transactional(propagation = Propagation.NEVER)

isolation(隔离级别)

解决事务并发执行的问题,微观上,2个执行时间相近的事务相互影响的问题。

  • 4种标准的隔离级别

隔离性在数据库的并发访问时得以体现,随着数据库并发事务处理能力的大大增强,数据库资源的利用率也会大大提高,从而提高了数据库系统的事务吞吐量,可以支持更多的用户并发访问。但并发事务处理也会带来一些问题,主要包含两方面的问题,

  1. 并发修改相同的数据 通过锁解决,1个用户在修改数据时其他用户想要修改必须等待
  2. 并发读写相同的数据如:脏读、不可重复读、幻读。这些问题是和隔离性相伴相生的。下面一一解释其含义。
  • 脏读B事务正在对一条记录做修改,在这个事务提交前,这条记录的数据就处于不一致状态;这时,另一个事务A也来读取同一条记录,如果不加控制,第二个事务读取了这些未提交的(“脏”)数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫作"脏读"(Dirty Reads)。
  • 不可重复读一个事务中多次执行相同的查询SQL,不同时刻读取的数据不同。要么发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫作“ 不可重复读”(Non-Repeatable Reads)。
  • 幻读一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”(Phantom Reads)。

上述三个问题通过事务的隔离级别解决。

隔离级别 含义 特点 存在的问题
READ-UNCOMMITTED 读未提交 一个事务能够读取到另一个事务没有提交的数据 脏读、不可重复读、幻读
READ-COMMITTED 读已提交 只能读到另外一个事务提交后的数据。 不可重复读、幻读
REPEATABLE-READ 可重复读 同一个事务下,前后两次结果一致 不同数据库不同,有的数据库会有幻读
SERIALIZABLE 序列化读 串行 性能太差

Oracle只支持READ-COMMITTED和SERIALIZABLE 2种隔离级别,不支持脏读,但是提出了一个只读的状态。

MySQL支持4种。

基本使用

1.在启动类加上@EnableTransactionManagement注解启用springboot事务
 启动类加上@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)注解解决同一个类中没有事务的方法调用其他方法@Transactional()
  不生效的问题(((XXXServiceImpl) AopContext.currentProxy()).save(User);) 该注解还可以解决类似的注解问题如@Async
2.在方法上加上事务注解
@Transactional(propagation = Propagation.MANDATORY)
public void aa(){

}

手动回滚事务

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

手动提交和回滚事务

第一种:利用sqlSession

  @Resource
    private SqlSessionTemplate sqlSessionTemplate;
    @Test
    @SneakyThrows
    public void test3(){
        SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession();
        Connection connection = sqlSession.getConnection();
        try {
            connection.setAutoCommit(false);
            ReimbUnified unified = new ReimbUnified();
            unified.setId("1");
            unified.setBillId("1");
            unified.setBillCode("1");
            unified.setBudgetId("1");
            unified.setIsDeleted(false);
            unified.setInvoiceAmount(BigDecimal.TEN);
            unified.setReimbAmount(BigDecimal.TEN);
            ReimbUnifiedMapper unifiedMapper = sqlSession.getMapper(ReimbUnifiedMapper.class);
            unifiedMapper.insert(unified);
            connection.commit();
        }catch (Exception e){
           connection.rollback();
        }finally {
            if (connection!=null){
                connection.close();
            }
        }

    }
}

第二种:利用TransactionTemplate

@Service
public class UserServiceImpl extends ServiceImpl<BaseMapper<User>,User> implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public void aa(){
        transactionTemplate.execute(status->{
            User user = new User();
            user.setId(1L);
            userMapper.insert(user);
            System.out.println(status.isNewTransaction());
            return null;
        });
    }
}

第三种:利用dataSourceTransactionManager

@Resource
private DataSourceTransactionManager dataSourceTransactionManager;

public void aa(){
    DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
    defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    TransactionStatus transaction = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);
    try{
        // 业务代码

        // 手动提交事务
        dataSourceTransactionManager.commit(transaction);
    } catch(Exception e) {
        // 手动回滚事务
        dataSourceTransactionManager.rollback(transaction);
    }
}

springboot 异步事务Transactional

使用场景:
我们在处理业务时会有这样的需求:我们需要在业务中需要调用远程的RPC接口,或者调用MQ发送消息,如果一切正常那自然皆大欢喜;但是,业务中出现了异常,导致了事务回滚,但是调用远程的RPC接口,或者调用MQ的动作是无法取消的,这可能就会出现严重的问题。
解决:在事务完成且提交后调用接口
第一种方式:

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
* @author wjj
*/
public class TransactionUtils {

public static void doAfterTransaction(DoTransactionCompletion doTransactionCompletion) {
if (TransactionSynchronizationManager.isActualTransactionActive()){
TransactionSynchronizationManager.registerSynchronization(doTransactionCompletion);
}
}

@Transactional
public void aa(){
TransactionUtils.doAfterTransaction(new DoTransactionCompletion(()->{
//send MQ ... RPC
}));
}
}

 

class DoTransactionCompletion implements TransactionSynchronization {
private Runnable runnable;
public DoTransactionCompletion(Runnable runnable) {
this.runnable = runnable;
}


@Override
public void afterCompletion(int status) {
if (status ==TransactionSynchronization.STATUS_COMMITTED){
this.runnable.run();
}
}
}

第二种方式:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
@Override
public void afterCompletion(int status) {

}
});

使用注意事项

使用注意事项(防止事务失效)

1、一个有@Transactional的方法被没有@Transactional方法调用时,会导致Transactional作用失效
原理:@Transactional是基于动态代理对象来实现的,而在类内部的方法的调用是通过this关键字来实现的,没有经过动态代理对象,所以事务回滚失效。

2、就是在@Transactional方法内部捕获了异常,没有在catch代码块里面重新抛出异常,事务也不会回滚。
原理:看看spring的源码:
TransactionAspectSupport类里面的invokeWithinTransaction方法

3、对非public方法进行事务注解,@Transactional 将会失效。
原因:是应为在Spring AOP代理时,事务拦截器在目标方法前后进行拦截,DynamicAdvisedInterceptor的intercept 方法会获取Transactional注解的事务配置信息,
因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,这个方法会获取Transactional 注解的事务配置信息。他会首先校验事务方法的修饰符是不是public,不是 public则不会获取@Transactional 的属性配置信息。

4、在一个类中A方法被事务注释,B方法也被事务注释。
现象:执行B方法是报错,但是异常被A catch 住,此时事务也会失效。