1. 目录

[TOC]

2. 介绍

当数据持久并非一次性入库的时候需要使用事务保证数据一致性。开启事务一般有两种模式:申明式事务和编程式事务

  • 声明式事务:直接使用@Transactional注解开启事务,其意义为进入注解方法时自动开启事务,内部运行及调用都在事务中,运行完成此方法时主动关闭事务,其好处是程序自动控制并且关闭连接,程序开发时也非常方便。
  • 编程式事务:在方法编写时手动开启事务,并且在用完之后需要手动关闭连接,否则连接不被释放。使用编程式事务大多在申明式事务不方便作用时,如在方法内部调用内部方法时,被调用的方法开启事务。

3. 原理

在spring中,@Transactional注解用于开启自动事务。
开启后spring启动时会在bean的创建时主动扫描@Transactional注解,并在initialBean方法中通过动态代理创建TransactionManagement类代理原类,从而保证加入容器的bean为代理之后的类。
在代码中注入bean的时候,实际上是注入的代理之后的bean,调用方法时首先调用代理类的代理方法,通过处理好代理方法后再执行原对象方法的方式加入事务管理,从而实现自动开启事务和关闭事务。

具体详细流程见源码分析

@Transactional 注解可以用于类或方法上,用于类上表示这个类的所有public方法都默认开启事务,用于方法上表示此类的此方法开启事务。

4. 使用

在spring中需要引入spring-tx依赖包,使用spirngboot项目现在已经默认不需要添加注解@EnableTransactionManagement即可开启。

在需要使用的类上加上@Transactional注解即可,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
//@Transactional
public class UucsUserService {

@Autowired
UucsUserMapper uucsUserMapper;

@Transactional
public UucsUser getUser(Integer id) {
UucsUser uucsUser = uucsUserMapper.selectByPrimaryKey(id);
List<Map> map = uucsUserMapper.testUnion();
System.out.println(map);
return uucsUser;
}
}

5. 事务的特性

5.1. 传播特性

事务是可以嵌套的,即方法已经开启事务了,但是方法在调用其他方法时,其他方法也开启了事务,这时就出现了事务嵌套,需要指定相应的传播特性,@Transactional中的propagation参数即为指定对应的传播特性。
事务的7大传播特性枚举Propagation如下:

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
30
31
32
public enum Propagation {

//如果有事务,那么加入事务,没有的话新建一个(默认没有指定的时候使用此)。
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
//不管原来有没有事务,此处都会新建一个事务,
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
//支持事务,有事务就以事务运行,没有事务就不开事务
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
//内嵌事务,有事务则新开一个事务,没有事务则不开启事务
NESTED(TransactionDefinition.PROPAGATION_NESTED),
//强制开启事务,没有事务会报错
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

//不开启事务
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
//不允许事务,如果有事务会报错
NEVER(TransactionDefinition.PROPAGATION_NEVER),

;

private final int value;


Propagation(int value) {
this.value = value;
}

public int value() {
return this.value;
}

}

5.2. 隔离级别

事务除了具有传播特性,还有隔离级别,其也是对应数据库的隔离级别

事务的四大隔离级别枚举Isolation如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum Isolation {

//默认的隔离级别,由具体数据库指定,程序不控制,一般使用此
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
//读未提交
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
//读已提交
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
//可重复读
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
//序列化读
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE)

;


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

其特性具体可以参见数据库的隔离级别

5.3. 事务回滚

@Transactional中支持配置回滚策略,即发生异常时可以回滚数据,不提交,spring默认的回滚策略为发生RuntimeException的时候。其配置属性为``robackFor`

因此,默认情况下抛出的异常只是Exception异常而非RuntimeExcecption时,回滚是不会生效的,也就是说会直接提交事务,提交修改的数据。针对此需要自己定义异常,如设置robackFor=Exception.classrobackFor=Threable.class

6. 使用@Transactional的注意事项

@Transactional注解虽然方便,但使用不当也会导致事务不生效,使用时需要注意其失效场景

  • @Transactional只能外部调用,不能类方法调用注解方法。原因是因为其动态代理原理,注入spring中的才是被代理过的类,只有通过容器的bean调用,才能走代理的相关逻辑,内部调用则是直接调用的内部方法,当然不会有任何效果。
  • 注意@Transactional的传播特性。如其特性为NOT_SUPPORTEDNEBVER,也不会生效
  • 注意@Transactional的回滚策略。如使用默认回滚策略RuntimeExcecption而又没有抛出运行时异常