引言
在 spring boot 中,事务管理是一个非常重要的功能,尤其是在涉及数据库操作的业务场景中。spring 提供了强大的事务管理支持,能够帮助我们简化事务的管理和控制。本文将详细介绍 spring boot 中事务的用法,包括事务的基本概念、事务的配置、事务的传播行为、事务的隔离级别以及事务的回滚机制。
1. 事务的基本概念
事务(transaction)是指一组数据库操作,这些操作要么全部成功,要么全部失败。事务的四大特性(acid)包括:
- 原子性(atomicity):事务中的所有操作要么全部成功,要么全部失败。
- 一致性(consistency):事务执行前后,数据库的状态保持一致。
- 隔离性(isolation):多个事务并发执行时,彼此之间互不干扰。
- 持久性(durability):事务一旦提交,对数据库的修改是永久性的。
在 spring boot 中,事务管理是通过 @transactional
注解来实现的。
2. spring boot 中事务的配置
2.1 启用事务管理
spring boot 默认已经集成了事务管理功能,只需要在配置类或启动类上添加 @enabletransactionmanagement
注解即可启用事务管理。
@springbootapplication @enabletransactionmanagement // 启用事务管理 public class myapplication { public static void main(string[] args) { springapplication.run(myapplication.class, args); } }
2.2 配置数据源和事务管理器
spring boot 默认使用 datasourcetransactionmanager
作为事务管理器。如果你使用的是 spring data jpa,事务管理器会自动配置。
# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: root driver-class-name: com.mysql.cj.jdbc.driver
3. 使用 @transactional 注解
@transactional
是 spring 提供的事务管理注解,可以标注在类或方法上。标注在类上时,表示该类中的所有方法都启用事务管理;标注在方法上时,表示该方法启用事务管理。
@service public class userservice { @autowired private userrepository userrepository; @transactional // 开启事务 表示该方法开启了事务 public void createuser(user user) { userrepository.save(user); } }
3.2 事务的传播行为
事务的传播行为(propagation)定义了事务方法之间的调用关系。spring 提供了以下几种传播行为:
- required(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。(适用于大多数业务场景,尤其是需要保证多个操作在同一个事务中执行的场景。 例如,订单创建时需要同时更新订单表和库存表。)
- requires_new:无论当前是否存在事务,都创建一个新的事务。(适用于需要独立事务的场景,尤其是日志记录、审计等操作。 例如,记录操作日志时,即使主事务失败,日志记录仍然需要成功。)
- supports:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。(适用于不需要强制事务的场景,例如查询操作。 例如,查询用户信息时,如果调用方有事务,则加入事务;如果没有事务,则以非事务方式执行。)
- not_supported:以非事务方式执行操作,如果当前存在事务,则挂起该事务。(适用于不需要事务支持的场景,例如发送消息、调用外部接口等。 例如,发送短信通知时,不需要事务支持。)
- mandatory:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(适用于强制要求调用方必须有事务的场景。 例如,某些核心业务逻辑必须在一个事务中执行。)
- never:以非事务方式执行操作,如果当前存在事务,则抛出异常。(适用于强制要求调用方不能有事务的场景。 例如,某些只读操作或外部调用。)
- nested:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。(适用于需要部分回滚的场景。 例如,订单创建时需要更新多个表,如果某个表更新失败,只需要回滚该表的操作,而不影响其他表的操作。)
示例:
@transactional(propagation = propagation.requires_new) public void updateuser(user user) { userrepository.save(user); }
3.3 事务的隔离级别
事务的隔离级别(isolation)定义了事务之间的可见性。spring 支持以下几种隔离级别:
- default:使用数据库的默认隔离级别。(适用于大多数通用场景,尤其是当你对数据库的默认行为没有特殊要求时。 如果你不确定应该选择哪种隔离级别,可以使用 default,让数据库根据其默认行为处理事务。)
- read_uncommitted:允许读取未提交的数据变更,可能会导致脏读、幻读和不可重复读。(适用于对数据一致性要求不高的场景,例如统计数据的读取或日志记录。 不适用于涉及资金、订单等对数据一致性要求高的场景。)
- read_committed:只能读取已提交的数据,可以避免脏读,但可能会导致幻读和不可重复读。(适用于大多数业务场景,尤其是对数据一致性有一定要求但不需要严格隔离的场景。 例如,电商系统中的订单查询、用户信息查询等。)
- repeatable_read:确保在同一事务中多次读取同一数据时结果一致,可以避免脏读和不可重复读,但可能会导致幻读。(适用于对数据一致性要求较高的场景,例如银行系统中的账户余额查询。 例如,在一个事务中多次读取同一账户的余额时,确保结果一致。)
- serializable:最高隔离级别,确保事务串行执行,可以避免脏读、幻读和不可重复读(适用于对数据一致性要求极高的场景,例如金融系统中的资金清算、库存管理等。 由于性能开销较大,通常只在必要时使用。)
示例:
@transactional(isolation = isolation.read_committed) public user getuserbyid(long id) { return userrepository.findbyid(id).orelse(null); }
3.4 事务的回滚机制
默认情况下,spring 会在方法抛出 runtimeexception
或 error
时回滚事务。如果需要自定义回滚规则,可以通过 rollbackfor(哪些异常回滚)
和 norollbackfor(哪些异常不会回滚)
属性来指定。
示例:
@transactional(rollbackfor = exception.class) // 所有异常都回滚 public void updateuser(user user) throws exception { userrepository.save(user); if (user.getname() == null) { throw new exception("用户名不能为空"); // 抛出受检异常 } }
4. 事务的嵌套与传播行为
在复杂的业务场景中,可能会存在事务方法调用事务方法的情况。此时,事务的传播行为决定了事务的嵌套方式。
4.1 嵌套事务示例
@service public class orderservice { @autowired private userservice userservice; @transactional //一级事务 public void createorder(order order) { // 保存订单 orderrepository.save(order); // 调用另一个事务方法 userservice.updateuser(order.getuser()); } } @service public class userservice { @transactional(propagation = propagation.requires_new) //二级事务 public void updateuser(user user) { userrepository.save(user); } }
在上面的示例中,createorder
方法调用 updateuser
方法时,updateuser
方法会开启一个新的事务。
5. 事务的注意事项(事务不生效的几种情况)
事务方法的可见性:
@transactional
只能应用于 public
方法。如果应用于 private
或 protected
方法,事务将不会生效。spring 的事务管理是基于代理模式实现的,代理对象只能拦截 public
方法。对于 private
或 protected
方法,spring 无法生成代理,因此事务不会生效。
@service public class userservice { @autowired private userrepository userrepository; // 正确:public 方法,事务生效 @transactional public void createuser(user user) { userrepository.save(user); } // 错误:private 方法,事务不会生效 @transactional private void updateuser(user user) { userrepository.save(user); } // 错误:protected 方法,事务不会生效 @transactional protected void deleteuser(long userid) { userrepository.deletebyid(userid); } }
事务的自我调用问题:
如果事务方法调用了同一个类中的另一个事务方法,事务的传播行为可能不会生效。这是因为spring 的代理对象只能拦截从外部调用的方法。如果事务方法在同一个类中调用另一个事务方法,实际上是直接调用目标方法,而不是通过代理对象调用,因此事务的传播行为不会生效。
@service public class orderservice { @autowired private orderrepository orderrepository; @transactional public void createorder(order order) { // 保存订单 orderrepository.save(order); // 调用另一个事务方法(自我调用) updateinventory(order.getproductid(), order.getquantity()); } @transactional(propagation = propagation.requires_new) public void updateinventory(long productid, int quantity) { // 更新库存逻辑 } }
在上面的示例中,createorder
方法调用了 updateinventory
方法,但由于是自我调用,updateinventory
方法的事务传播行为(requires_new
)不会生效。
解决方法:
将事务方法拆分到不同的类中:
将 updateinventory
方法移到另一个服务类中,通过依赖注入调用。
@service public class orderservice { @autowired private orderrepository orderrepository; @autowired private inventoryservice inventoryservice; @transactional public void createorder(order order) { // 保存订单 orderrepository.save(order); // 调用另一个服务类的事务方法 inventoryservice.updateinventory(order.getproductid(), order.getquantity()); } } @service public class inventoryservice { @transactional(propagation = propagation.requires_new) public void updateinventory(long productid, int quantity) { // 更新库存逻辑 } }
事务的超时设置:
可以通过 @transactional(timeout = 10)
设置事务的超时时间(单位为秒)。如果事务执行时间超过指定时间,事务将自动回滚。
@service public class reportservice { @autowired private reportrepository reportrepository; @transactional(timeout = 10) // 设置事务超时时间为 10 秒 public void generatereport() { // 模拟耗时操作 for (int i = 0; i < 1000000; i++) { reportrepository.save(new report("report " + i)); } } }
在上面的示例中,如果 generatereport
方法的执行时间超过 10 秒,事务将自动回滚。
6. 总结
spring boot 提供了强大的事务管理功能,通过 @transactional
注解可以轻松实现事务的控制。在实际开发中,需要根据业务需求选择合适的传播行为和隔离级别,同时注意事务方法的可见性和自我调用问题。
通过本文的介绍,相信你已经掌握了 spring boot 中事务的基本用法。如果你有更多问题,欢迎在评论区留言讨论!
到此这篇关于spring boot 中事务的用法详解的文章就介绍到这了,更多相关spring boot 事务的用法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论