本文最后更新于 2025年6月24日 晚上
什么是事务 事务,是系统在处理业务时,为保障数据一致性、完整性与可靠性 而设计的一种逻辑单元抽象。它源于现实中的业务流程抽象,如:
但在现代系统中,业务请求通常会被分解为一系列跨越多个系统组件的处理过程,例如:
1 2 3 4 5 6 7 8 rust 复制编辑 Customer(请求) -> Web代理 -> 应用服务 -> 缓存服务器 -> 消息队列 -> 数据库服务
事务的目标就是在这种复杂系统流转中,确保数据的 正确性和一致性 。
事务的 ACID 特性
特性
说明
原子性(Atomicity)
要么全部成功,要么全部失败
一致性(Consistency)
事务执行前后,系统状态合法且符合业务规则
隔离性(Isolation)
多个事务互不干扰,互不影响
持久性(Durability)
事务提交后的结果永久保存
不同组件中的事务处理机制 代理服务与缓存服务器
代理层 :一般只做请求转发,不参与事务控制;
缓存层 :通常处理静态数据,对核心业务一致性要求低,事务性不强,重点在于 缓存一致性与失效策略 。
消息队列中的事务处理
主要通过可靠投递机制 保障最终一致性;
通过 事务消息机制 (如RocketMQ)支持本地事务与消息状态协调;
通过幂等设计、重复消费保护 提升整体稳定性。
以 RocketMQ 为例的事务消息流程:
发送“预处理消息”到MQ。
执行本地事务(如数据库写入)。
根据事务执行结果向MQ提交 commit 或 rollback。
MQ端若长时间未收到确认,将主动发起事务状态回查。
应用服务器中的事务管理
通过应用框架提供的事务管理器(如Spring @Transactional)划定事务边界;
提供事务传播机制(如REQUIRED、REQUIRES_NEW等)管理嵌套事务;
支持跨资源事务管理(如JTA、XA协议)协调多数据库或外部资源的事务一致性。
数据库中的事务支持
通过事务日志(如WAL)、Redo Log、Undo Log保障持久性;
通过锁机制(行锁、表锁)、隔离级别保障并发控制;
支持 savepoint、rollback、commit 等事务控制语句。
业务流程中的事务处理 使用DataSource 完成与SQL的通信 在SpringBoot中,定义DataSource 参数
1 2 3 4 5 spring.application.name =aotspring.datasource.url =jdbc:mysql://localhost:3306 /springbootspring.datasource.username =rootspring.datasource.password =123456 spring.datasource.driver-class-name =com.mysql.jdbc.Driver
直接可以得到数据库连接对象
1 2 3 4 @Test void contextLoads (@Autowired DataSource datasource) throws Throwable{ System.out.println(datasource.getConnection()); }
在Spring中需要配置连接类,定义dataSource类
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 33 34 35 36 37 38 39 package com.codfish.aot.spring;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DriverManagerDataSource;import javax.sql.DataSource;@Configuration public class DataSourceConfig { @Value("${spring.datasource.url}") private String url ; @Value("${spring.datasource.username}") private String username ; @Value("${spring.datasource.password}") private String password ; @Value("${spring.datasource.driver-class-name}") private String driverClassName ; @Bean public DataSource dataSource () { DriverManagerDataSource dataSource = new DriverManagerDataSource (); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); return dataSource; } }@Test void contextLoads2 (@Autowired DataSourceConfig datasource) throws Throwable{ System.out.println(datasource.dataSource()); }
创建JDBCTemplate 完成数据库命令执行 SpringBoot :
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 package com.codfish.aot.spring;public class User { private String name; private Integer id; public String getName () { return name; } public void setName (String name) { this .name = name; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } @Override public String toString () { return "User{ name=" +name+ " ID=" + id + '}' ; } }@Test void testJdbctemplate (@Autowired JdbcTemplate jdbcTemplate) throws SQLException { String sql = "select * from student where id=?" ; User user = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper <>(User.class),2 ); System.out.println(user); }
Spring :
1 2 3 4 5 6 7 8 9 10 11 12 13 @Bean public JdbcTemplate jdbcTemplate (DataSource dataSource) { return new JdbcTemplate (dataSource); } @Test void testJdbctemplate (@Autowired JdbcTemplate jdbcTemplate) throws SQLException { String sql = "select * from student where id=?" ; User user = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper <>(User.class),2 ); System.out.println(user); }
事务 SpringBoot : 直接向需要使用事务的方法加入@Transactional注解
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 @Repository public class UserDao { @Autowired JdbcTemplate jdbcTemplate; public int insert () { return jdbcTemplate.update("insert into student(ID,NAME) values(?,?)" ,3 ,"godfish" ); } public int update () { return jdbcTemplate.update("update student set NAME=? where ID=?" , "codfish" ,1 ); } public int delete () { return jdbcTemplate.update("delete from student where ID=?" ,1 ); } public User query () { return jdbcTemplate.queryForObject("select * from student where ID=?" , new BeanPropertyRowMapper <>(User.class),1 ); } } @Service public class UserService { @Autowired UserDao userDao; @Transactional public int insert () { return userDao.insert(); } }
在Spring中 需要启动EnableTransactionManagement 注解
1 @EnableTransactionManagement
并在业务中定义一个TransactionManager的 Bean
1 2 3 4 @Bean public TransactionManager transactionManager (DataSource dataSource) { return new DataSourceTransactionManager (dataSource); }
事务的隔离级别 在多线程数据访问过程中,多个查询事务可以会存在相互间的冲突,导致数据存在不一致的情况
丢失更新
脏读
不可重复读
幻读
Read Uncommit
x
√
√
√
Read Commited
x
x
√
√
通过行锁在事务期间锁定读操作
Repeatable Read
x
x
x
√
通过行锁在事务期间锁定读写
Serializable
x
x
x
x
通过表锁在事务操作期间锁定
事务传播 在业务过程中存在某个方法被定义为事务之后其调用的后续方法也通过事务执行,这时通过设置不同的isolation 来控制 多个事务之间的行为
外部请求不存在事务
外部请求存在事务
REQUIRED
开启新的事务
融入外部事务
@Transactional(propagation=Progagation.REQUIRED) 增删改查
SUPPORTS
不开启新的事务
融入外部事物
@Transactional(propagation=Progagation.SUPPORT) 适合查询
REQUIRES_NEW
开启新的事务
开启新的事务
@Transactional(propagation=Progagation.REQUIRES_NEW) 适用于与外部事务无关的情况
NOT_SUPPORT
不开启新的事务
不用外部事务
@Transactional(propagation=Progagation.NOT_SUPPORT)
NEVER
不开启新的事务
抛出异常
@Transactional(propagation=Progagation.NEVER)
MANDATORY
抛出异常
融合到外部事务中
@Transactional(propagation=Progagation.MANDATORY)
NESTED
开启新的事务
融合到外部事务中,SAVEPOINT
@Transactional(propagation=Progagation.NESTED)
readOnly 只会设置在查询的业务方法中,具体执行效率视数据库类型而定
timeout 设置事务等待的最长时间
noRollbackFor / RollbackFor 决定哪些 异常会回滚,哪些异常不会回滚
默认RuntimeException 及其子类异常会回滚
事务失效
保证事务的类配置为一个bean
事务方法不能是一个private
事务执行期间的异常被自行捕捉
只有动态代理过的对象才可以正常执行事务
解决 1. 暴露自身包装为动态代理对象(循环依赖)
1 2 3 2. AopContext.currentProxy()3. 创建一个Bean ,执行DAO对象 再将该对象引用进来执行
本质原因在于 Spring AOP 是基于代理机制实现事务增强的:自调用绕过代理、异常捕获、非 public 方法、未被 Spring 管理的对象均绕过了事务代理逻辑。