Spring事务
参考:
小姐姐非要问我:spring编程式事务是啥? (qq.com)
Spring系列第44篇:详解spring声明式事务(@Transactional)路人甲Java的博客-CSDN博客spring 声明式事务
1.事务分类
jdbcTemplate:
一个模块,对jdbc操作进行了封装,使其更简单,就是本文要讲的JdbcTemplate,JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。
-
编程式事务:程序员自己控制事务
- 方式1:通过PlatformTransactionManager控制事务
- 方式2:通过TransactionTemplate控制事务
-
声明式事务:使用注解或配置文件声明事务,不关心事务具体流程:获取连接,关闭连接,事务提交,回滚,异常处理
声明式事务的缺点:最小粒度为方法
编程式事务模型:
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
//2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
//3.开启事务:调用platformTransactionManager.getTransaction开启事务操作,得到事务状态(TransactionStatus)对象
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
//4.执行业务操作,下面就执行2个插入操作
try {
System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
//5.提交事务:platformTransactionManager.commit
platformTransactionManager.commit(transactionStatus);
} catch (Exception e) {
//6.回滚事务:platformTransactionManager.rollback
platformTransactionManager.rollback(transactionStatus);
}
2.Spring事务的5个要素
隔离级别,传播行为,回滚规则,事务超时,只读
隔离级别
与数据的级别完全一致,如果spring配置的隔离级别和数据库冲突的时候,以spring为准
| 级别 | 含义 |
|---|---|
| ISOLATION_DEFAULT | 使用数据库默认的隔离级别(Mysql:Repeatable_read) |
| ISOLATION_READ_UNCOMMITTED | 读未提交,脏读、幻读或不可重复读 |
| ISOLATION_READ_COMMITTED | 读已提交,幻读或不可重复读 |
| ISOLATION_REPEATABLE_READ | 可重复读,幻读 |
| ISOLATION_SERIALIZABLE | 可穿化 |
传播行为
事务中调用了另一个事务,那么到底是新开一个事务,还是继续在这个事务内部处理,亦或是其他等等,这都是由传播行为所决定的
| 传播行为 | 含义 |
|---|---|
| PROPAGATION_REQUIRED | 当前方法必须运行在事务中。 如果当前存在事务,加入已存在的事务中,否则启动一个新事务。 |
| PROPAGATION_SUPPORTS | 当前方法支持运行在事务中。 如果当前存在事务,加入已存在的事务中,否则非事务运行。 |
| PROPAGATION_MANDATORY | 当前方法必须运行在事务中。 如果当前存在事务,加入已存在的事务中,否则抛出异常。 |
| PROPAGATION_REQUIRED_NEW | 当前方法必须运行在自己的事务中。 如果当前存在事务,挂起已存在的事务中,启动一个新事务执行。 |
| PROPAGATION_NOT_SUPPORTED | 当前方法不支持运行在事务中。 如果当前存在事务,挂起已存在的事务中,非事务运行。 |
| PROPAGATION_NEVER | 当前方法不能运行在事务中。 如果当前存在事务,抛出异常。 |
| PROPAGATION_NESTED | 类似于 PROPAGATION_REQUIRED_NEW,但前者是两个独立的事务,而它是嵌套事务。 |
PROPAGATION_REQUIRES_NEW 启动一个新的,不依赖于环境的 “内部” 事务。这个事务将被完全 commited 或 rolled back 而不依赖于外部事务,它拥有自己的隔离范围, 自己的锁等等。当内部事务开始执行时,外部事务将被挂起, 内务事务结束时,外部事务将继续执行。 PROPAGATION_NESTED 开始一个 “嵌套的” 事务,它是已经存在事务的一个真正的子事务。 嵌套事务开始执行时,它将取得一个 savepoint
回滚规则
指定哪些异常需要做事务回滚
事务超时
事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
@Transactional(timeout = 1) 单位s -1代表不设置超时时间
只读
如果事务只对数据库进行读操作,数据库可以利用事务的只读特性来进行一些特定的优化,例如优化掉不必要的锁操作等等。
3.声明式事务使用的5个步骤
- 1.开启Spring事务管理,在配置类上增加 @EnableTransactionManagerment注解
当spring容器启动的时候,发现有@EnableTransactionManagement注解,此时会拦截所有bean的创建,扫描看一下bean上是否有@Transaction注解(类、或者父类、或者接口、或者方法中有这个注解都可以),如果有这个注解,spring会通过aop的方式给bean生成代理对象,代理对象中会增加一个拦截器,拦截器会拦截bean中public方法执行,会在方法执行之前启动事务,方法执行完毕之后提交或者回滚事务。
- 2.定义事务管理器
Spring不会直接对事务进行管理,而是通过事务管理器接口PlatformTransactionManager完成
| TransactionManager | Description |
|---|---|
| DataSourceTransactionManager | 适用 JDBC 或 iBatis(MyBatis) |
| HibernateTransactionManager | 适用 Hibernate 3 及以上版本 |
| JpaTransactionManager | 适用 JPA |
| JtaTransactionManager | 使用一个 JTA 实现来管理事务,在一个事务跨越多个资源时使用 |
- 3.在事务上增加@Transaction注解
@Transaction放在接口上,那么接口的实现类中所有public都被spring自动加上事务
@Transaction放在类上,那么当前类以及其下无限级子类中所有pubilc方法将被spring自动加上事务
@Transaction放在public方法上,那么该方法将被spring自动加上事务
注意:@Transaction只对public方法有效
@Transaction事务的主要参数
| 参数 | 描述 |
|---|---|
| value | 指定的事务管理器的bean名称,如有多个PlatformTransactionManager的实现,需要指定唯一的 |
| transactionManager | 同value,二者选配一个 |
| propagation | 事务传播机制 |
| isolation | 事务的隔离级别,默认是default |
| timeout | 事务执行的超时时间 |
| readOnly | 是否为只读事务,例如查询操作,可以指定为只读,提升查询速度 |
| rollbackfor | 定义异常,抛出该异常的时候,spring会让事务回滚,默认为RuntimeException和error |
| rollbackForClassName | 同rollbackfor指定方式为类名 |
| norollbackfor | 定义不回滚的异常 |
| norollbackForClassName | 定义不回滚的异常类名 |
- 4.定义事务方法
在@Transaction注解中添加业务操作
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
//先清空表中数据,然后批量插入数据,要么都成功要么都失败
@Transactional
public void insertBatch(String... names) {
jdbcTemplate.update("truncate table t_user");
for (String name : names) {
jdbcTemplate.update("INSERT INTO t_user(name) VALUES (?)", name);
}
}
}
- 5.调用事务方法
4.声明式事务注意点
4.1 仅对 public 方法有效
只有 @Transactional 注解应用到 public 方法上才能进行事务管理。这是因为 Spring 在 AOP 事务注解时,在读取注解上的属性方法中,会优先判断方法是否是 public,如果不是 public,就不会读取事务配置信息
4.2 AOP 的自调用问题
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理。也就是说,在同一个类中的一个 @Transactional 方法中,去掉用另一个 @Transactional 方法,会导致第二个方法的事务无效,被 Spring AOP 所忽略。