默认情况下,Spring Boot 事务是自动提交的。每个 SQL 语句都在自己的事务中执行并在执行后提交。请看下面的示例,即使发生异常,产品也会被插入数据库中。
这不应该是这样的。在某些情况下,多个事务在一个逻辑代码单元中运行。只有在没有异常发生的情况下,所有事务才应该提交。
在 Spring Boot 中,当使用 @Transactional 注释时,Spring Boot 隐式创建一个代理,该代理将创建一个连接到数据库。在执行代码时,将启动并提交事务,如果出现异常,则将回滚更改。
示例 1
在此示例中,我们添加了 @Transactional 注释,以便在出现异常时回滚事务。
这个例子证明了 @Transactional 注释可以在出现异常时回滚事务。请注意,Spring 默认只会在未检查到异常时回滚。
示例 2a
此示例显示,即使我们指定了 @Transactional 注释,已检查的异常也不会被回滚。
示例 2b
要回滚已检查异常,我们需要指定 <em>rollbackFor</em>
。
我们看到事务已成功回滚。
示例 3
在此示例中,我们添加了 try-catch 块以查看回滚是否仍在工作。
这不起作用。这是因为我们手动捕获了异常并处理它。因此,当前事务正在正常执行并提交。
示例 4
那么 嵌套事务 呢?从下面的示例中,createProduct() 和 createOrder() 方法将在数据库中插入记录。createOrder() 方法抛出了运行时异常。这两个方法都已用 @Transactional 注释进行了注释。
createProduct() 和 createOrder() 事务被回滚了。即使 createOrder() 方法发生了运行时异常。但是,由于它没有处理异常,这导致 createProduct() 事务也回滚。
示例 5
让我们添加 try-catch 块以处理 createProduct() 方法中的运行时异常。
奇怪?为什么两个记录都被回滚了?同时,我们也注意到了异常错误。
@Transactional 有一个名为 Propagation 的参数。Propagation 将定义事务边界。Spring 将根据传播设置启动和结束事务。默认情况下,传播设置为 REQUIRED。因此,如果存在活动事务,则 Spring 将使用它而不是创建新事务。因此,在上述场景中,即使我们在外部方法中捕获了异常,事务仍然会回滚。
示例 6
使用 Propagation REQUIRES_NEW,这将强制 Spring 创建一个子事务。
产品记录被写入数据库,订单记录被回滚。
示例 7
如果我们从未在外部方法中使用 try 和 catch 块,并且在内部方法中发生了异常,外部事务仍会提交吗?
我们看到两个事务都被回滚了。因为内部事务抛出了异常,外部事务检测到异常并且未被处理。因此,外部事务已被回滚。
示例 8
让我们看看异常是否发生在外部方法中。
@Transactional
public void createProduct() {
System.out.println("------ createProduct ------");
Product prod = new Product();
prod.setDescription("This is createProduct method.");
prod.setPrice(10);
prod.setTitle("Create Product with runtime");
productRepository.save(prod);
orderController.createOrder();
throw new RuntimeException("Create Product RuntimeException");
}@Transactional(propagation=Propagation.REQUIRES_NEW)
public void createOrder() {
System.out.println("------ createOrder ------");
Order order = new Order();
order.setTitle("Create Order with propagation required_new");
order.setDescription("This is createOrder method.");
orderRepository.save(order);
}
我们看到,由于 Propagation.REQUIRES_NEW,内部事务已成功提交。这迫使Spring在子事务中处理查询。一旦完成,外部事务将恢复,并且在此时发生异常,因此它被回滚。
附加说明
请注意,如果两个方法在同一个类中,则 @Transactional 注释将不会生效。它始终被视为一个事务,因此即使我们指定了 Propagation.REQUIRES_NEW,它也不会起作用。一开始,我们谈到了Spring在看到注释 @Transactional 时创建代理。当你调用内部方法时,它将绕过代理。
谢谢阅读!
评论(0)