首先我们要明白Spring 事务是什么?
Spring 事务是 Spring 框架提供的事务管理机制,用于保证数据库操作的原子性、一致性、隔离性和持久性(ACID 特性) 。它简化了传统 Java EE 中复杂的事务管理(如手动编写 try-catch 控制事务),通过声明式或编程式方式,让开发者更专注于业务逻辑。
常见面试题及解析
1. Spring 事务的核心特性是什么?
Spring 事务的核心是保证数据库操作的ACID 特性:
- 原子性(Atomicity) :事务中的操作要么全部成功,要么全部失败(回滚)。
- 一致性(Consistency) :事务执行前后,数据库状态始终符合业务规则(如转账后总金额不变)。
- 隔离性(Isolation) :多个并发事务之间相互隔离,避免干扰(如脏读、不可重复读、幻读)。
- 持久性(Durability) :事务提交后,数据修改永久保存到数据库。
2. Spring 事务的管理方式有哪些?
Spring 支持两种事务管理方式:
-
编程式事务:通过代码手动控制事务(如
TransactionTemplate或PlatformTransactionManager),灵活性高但侵入性强。java
运行
@Autowired private TransactionTemplate transactionTemplate; public void doBusiness() { transactionTemplate.execute(status -> { // 业务逻辑 return null; }); } -
声明式事务:通过注解(
@Transactional)或 XML 配置声明事务,非侵入式,是实际开发的首选。java
运行
@Transactional public void doBusiness() { // 业务逻辑 }
3. @Transactional注解的核心属性有哪些?
@Transactional的常用属性用于控制事务行为:
- propagation:事务传播行为(如
REQUIRED、REQUIRES_NEW,控制多个事务方法调用时的事务关系)。 - isolation:事务隔离级别(如
READ_COMMITTED,控制并发事务的隔离程度)。 - timeout:事务超时时间(默认 - 1,永不超时,单位秒)。
- readOnly:是否为只读事务(若为
true,数据库可优化性能,适用于查询操作)。 - rollbackFor:指定哪些异常触发回滚(默认只回滚
RuntimeException和Error)。 - noRollbackFor:指定哪些异常不触发回滚。
4. 事务传播行为有哪些?最常用的是哪两个?
事务传播行为定义了当一个事务方法调用另一个事务方法时,事务如何传播(是否共用一个事务,还是新建事务)。Spring 定义了 7 种传播行为:
-
REQUIRED(默认):如果当前有事务,则加入;否则新建事务。 -
REQUIRES_NEW:无论当前是否有事务,都新建一个事务(原事务暂停)。 -
SUPPORTS:如果当前有事务,则加入;否则以非事务方式执行。 -
MANDATORY:必须在事务中执行,否则抛异常。 -
NOT_SUPPORTED:以非事务方式执行,若当前有事务则暂停。 -
NEVER:以非事务方式执行,若当前有事务则抛异常。 -
NESTED:如果当前有事务,则嵌套在当前事务中(仅部分数据库支持,如 MySQL 的 Savepoint)。
最常用:REQUIRED(大多数业务场景)和REQUIRES_NEW(需要独立事务的场景,如日志记录)。
5. 事务隔离级别有哪些?Spring 默认的隔离级别是什么?
隔离级别解决并发事务中的脏读、不可重复读、幻读问题,Spring 定义了 5 种(对应数据库标准):
-
DEFAULT(默认):使用数据库默认隔离级别(如 MySQL 默认REPEATABLE_READ,Oracle 默认READ_COMMITTED)。 -
READ_UNCOMMITTED:允许读取未提交的数据(可能脏读、不可重复读、幻读)。 -
READ_COMMITTED:只能读取已提交的数据(避免脏读,可能不可重复读、幻读)。 -
REPEATABLE_READ:保证多次读取同一数据一致(避免脏读、不可重复读,可能幻读)。 -
SERIALIZABLE:最高隔离级别,事务串行执行(避免所有问题,但性能极差)。
Spring 默认隔离级别:DEFAULT(依赖数据库)。
6. @Transactional注解不生效的常见场景有哪些?
-
方法不是 public:
@Transactional默认只对 public 方法生效(非 public 方法事务不生效)。 -
自调用问题:同一个类中,非事务方法调用事务方法(因 Spring AOP 代理特性,事务不生效)。
java
运行
public class Service { public void methodA() { methodB(); // 自调用,methodB的@Transactional不生效 } @Transactional public void methodB() {} } -
异常被捕获:事务方法中异常被
try-catch捕获且未重新抛出,Spring 无法感知异常,不会回滚。 -
错误的异常类型:默认只回滚
RuntimeException和Error,若抛出 checked 异常(如IOException)且未通过rollbackFor指定,事务不回滚。 -
数据库不支持事务:如使用 MySQL 的 MyISAM 引擎(不支持事务),需改为 InnoDB。
-
注解加在接口或父类上:若代理方式为 CGLIB(代理类),接口上的
@Transactional可能不生效(建议加在实现类方法上)。
7. Spring 事务的实现原理是什么?
Spring 事务基于AOP(面向切面编程) 和动态代理实现:
- 当方法标注
@Transactional时,Spring 通过 AOP 生成代理对象,在方法执行前后插入事务管理逻辑(开启、提交、回滚)。 - 代理方式:若目标类实现接口,默认用JDK 动态代理;否则用CGLIB 代理(需配置
proxyTargetClass=true)。 - 核心接口:
PlatformTransactionManager(事务管理器,如DataSourceTransactionManager用于 JDBC 事务)。
8. 什么是事务的 “脏读、不可重复读、幻读”?如何解决?
- 脏读:事务 A 读取到事务 B 未提交的修改(B 可能回滚,导致 A 读错)。
解决:隔离级别至少READ_COMMITTED。 - 不可重复读:事务 A 多次读取同一数据,期间事务 B 修改并提交,导致 A 两次读取结果不一致。
解决:隔离级别至少REPEATABLE_READ。 - 幻读:事务 A 按条件查询,期间事务 B 新增符合条件的数据并提交,导致 A 再次查询时多了 “幻影” 数据。
解决:隔离级别SERIALIZABLE(或通过数据库锁机制,如 MySQL 的 Next-Key Lock)。
9. 如何实现分布式事务?Spring 有哪些方案?
分布式事务指跨多个数据库(或服务)的事务(如微服务中订单服务和库存服务的操作)。Spring 相关方案:
- 2PC(两阶段提交) :基于
JtaTransactionManager,依赖分布式事务协调器(如 Atomikos),但性能差、可用性低。 - TCC(Try-Confirm-Cancel) :业务层实现三个接口(尝试、确认、取消),如 Seata 的 TCC 模式。
- Saga 模式:将分布式事务拆分为本地事务序列,通过补偿机制回滚,适用于长事务。
- 本地消息表:基于消息队列实现最终一致性(如 RocketMQ 的事务消息)。
10. @Transactional的readOnly=true有什么作用?
- 标记事务为只读事务,告诉数据库此事务只做查询,不做修改。
- 数据库可优化性能(如 MySQL 的 InnoDB 会避免加锁,提高查询效率)。
- 若在
readOnly=true的事务中执行写操作,可能抛异常(取决于数据库)。