Java中设置TRANSACTION_READ_COMMITTED仍读脏数据,主因是连接池autoCommit=true未关闭;事务隔离级别仅在autoCommit=false时生效,否则每条SQL独立提交,setTransactionIsolation()被忽略。

Java里设置TRANSACTION_READ_COMMITTED为什么还是读到脏数据?
不是JDBC驱动或Spring没生效,大概率是数据库连接池自动提交(autoCommit=true)没关。事务隔离级别只在autoCommit=false时起作用——否则每条SQL都是独立事务,setTransactionIsolation()直接被忽略。
- 检查
DataSource配置:HikariCP要设connection-init-sql=SET autocommit = 0,或初始化后手动调用connection.setAutoCommit(false) - Spring Boot中
@Transactional默认会关autoCommit,但若手动获取Connection(比如用JdbcTemplate.execute(ConnectionCallback)),必须自己关 - MySQL 8.0+ 默认
autocommit=1,PostgreSQL也一样;别信“数据库已设好”,应用层连接池才是关键
SELECT ... FOR UPDATE在MySQL InnoDB下为何不阻塞幻读?
因为SELECT ... FOR UPDATE默认只加行锁(Record Lock),对间隙(Gap)不锁——而幻读本质是新插入的记录落在查询范围的间隙里。要真正防幻读,得用SELECT ... FOR UPDATE LOCK IN SHARE MODE(MySQL 8.0+)或更稳妥的SELECT ... FOR UPDATE配合唯一索引+当前读语义,但最可靠的是升级到TRANSACTION_SERIALIZABLE或改用INSERT ... SELECT前先SELECT COUNT(*)做存在性校验。
- 普通二级索引上的
FOR UPDATE只锁查到的行,不锁间隙;主键或唯一索引上才可能触发间隙锁(取决于查询条件是否能唯一定位) - MySQL的
REPEATABLE READ靠MVCC+间隙锁模拟串行,但仅对UPDATE/DELETE/INSERT生效,纯SELECT不触发间隙锁 - Spring的
@Transactional(isolation = Isolation.REPEATABLE_READ)只是声明,实际行为由数据库引擎决定,InnoDB和MySQL Server版本强相关
Java应用里TransactionDefinition.ISOLATION_SERIALIZABLE真能避免所有并发问题?
不能。它只是把并发控制推给数据库,而不同DB实现差异极大:PostgreSQL用真正的可串行化快照(SISI),MySQL InnoDB的SERIALIZABLE会把所有SELECT转成SELECT ... LOCK IN SHARE MODE,导致大量锁等待甚至死锁;H2内存库则直接禁用并发写入。结果往往是吞吐暴跌,而不是问题消失。
- MySQL开启
SERIALIZABLE后,原本无锁的简单查询也会持锁直到事务结束,极易引发Lock wait timeout exceeded - Spring的
ISOLATION_SERIALIZABLE在JDBC层只是调用setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE),不保证语义等价 - 真正需要强一致的场景(如库存扣减),应优先用乐观锁(
version字段+WHERE version = ?)或分布式锁,而非盲目升隔离级别
Spring Boot里@Transactional方法抛出异常却不回滚?
默认只对RuntimeException和Error回滚,受检异常(Exception子类但非RuntimeException)不会触发回滚——这是JDBC事务规范决定的,不是Spring bug。
立即学习“Java免费学习笔记(深入)”;
- 显式声明:
@Transactional(rollbackFor = {IOException.class, SQLException.class}) - 别依赖
throws声明,Spring只看实际抛出的异常类型,不看方法签名 - 自调用失效:同一类内
this.methodB()调用@Transactional方法methodB,代理未介入,事务根本没启动 - 异步方法(
@Async)默认脱离当前事务上下文,需用TransactionSynchronizationManager手动传播
事务不是魔法开关,它是数据库连接、线程上下文、异常类型、代理机制四者咬合的结果。漏掉其中任一环,隔离级别再高也没用。










