简介
本文的介绍主要从JDBC事务API的使用,到常用的SpringBoot中的事务管理,而SpringBoot其实是简化了很多Spring事务管理的配置。
JDBC的事务管理
基本的开启、提交和回滚事务
先复习一下JDBC的事务管理:
public void jdbcSaveOrder() throws SQLException {
// 1. 拿到JDBC连接
Connection connection = dataSource.getConnection();
try (connection) {
// 2. 设置自己控制事务
connection.setAutoCommit(false);
....
// 3. 提交事务
connection.commit();
}catch (Exception e){
log.error("ds save order error:", e);
// 4. 回滚事务
connection.rollback();
}
}
JDBC控制事务的API总体比较直观。
Spring的事务管理
可以从上面的JDBC示例中看到,事务管理最主要的几个操作就是:开启事务、提交事务、回滚事务。那么Spring是如何管理的呢?首先可以从不太常见的Spring编程式事务管理入手。
Spring编程式事务管理
我们可以通过PlatformTransactionManager
来编程式开启事务,不过Spring提供了一个更加简便的TransactionTemplate
:
@Service
public class OrderService {
@Autowired
private TransactionTemplate transactionTemplate;
public void programmaticTransactionSaveTwoOrder(){
transactionTemplate.executeWithoutResult( status -> {
var o = newOrder();
orderRepository.save(o);
o = newOrder();
orderRepository.save(o);
});
}
}
可以看到Spring已经简化了一部分事务管理的代码,比如:
- 不再需要自己关闭connection
- 不再需要自己开启、提交和回滚事务
并且在Spring中已经配置好的相关事务配置,也会继续起作用。但是相对来说,还有更常见也更简单的方法,那就是@Transactional
注解。
Spring声明式(@Transactional注解)事务管理
更常见的事务管理如下:
@Transactional
public void saveTwoOrder(){
var o = newOrder();
orderRepository.save(o);
o = newOrder();
orderRepository.save(o);
}
直接在需要事务管理的方法上@Transactional
方法就可以(如果是老项目,只使用了Spring而没有SpringBoot,需要再配置@EnableTransactionManagement
注解)。
需要注意的是,@Transactional
注解在private方法上是不生效的,如果使用intellij idea,那在使用这种错误方式的时候,IDE就会有相应的提示。
@Transactional
注解还有事务传播级别属性需要注意,如下所示:
// 默认传播级别
@Transactional(propagation = Propagation.REQUIRED)
这个传播级别属性并不属于JDBC,而是属于Spring的事务管理。propagation
属性值如下:
- REQUIRED (默认):支持事务,如果当前不存在事务则新建一个
- SUPPORTS:支持事务,如果当前不存在事务则直接执行
- MANDATORY:支持事务,如果当前不存在事务则抛出异常
- REQUIRES_NEW:创建新的事务,如果当前存在一个事务,则挂起
- NOT_SUPPORTED:不支持事务,如果当前存在一个事务,则挂起
- NEVER:不支持事务,如果当前存在一个事务,则抛出异常
- NESTED:嵌套事务,如果当前存在一个事务,则嵌套在当前事务中(Save Point)
@Transactional注解使用注意事项
回滚策略
Spring默认只会对RuntimeException
异常进行回滚,对检查异常Exception
则不会回滚。
如果需要对检查异常也进行回滚,需要修改一下注解的属性:
@Transactional(rollbackFor = Exception.class)
没有被@Transactional注解的方法内部直接调用被@Transactional注解的方法
查看如下示例:
@Slf4j
@Service
public class OrderService {
public void prepareAndSaveOrder(){
log.info("prepare...");
saveOrder(); //调用被@Transactional注解的方法
}
@Transactional
public void saveOrder() {
var o = newOrder();
orderRepository.save(o);
}
}
如果有人直接调用OrderService的prepareAndSaveOrder
方法,那么在saveOrder
上的注解就不会生效。这是因为如果直接内部调用saveOrder
,就会绕过Spring的AOP机制,从而使@Transactional
不生效。
不过也有一些技巧可以绕过这个限制,如注入Bean自身:
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderService orderService; // 注入当前Bean本身
public void prepareAndSaveOrder(){
log.info("prepare...");
orderService.saveOrder(); // 通过注入的orderService调用saveOrder
}
@Transactional
public void saveOrder() {
// .....
}
}
这时注意每次使用时,都必须使用orderService.
来调用对应方法。
也可以直接在prepareAndSaveOrder
方法上也加上@Transactional
注解。
参考资料
[1]Spring文档:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
评论
发表评论