read_committed能防止脏读但无法避免不可重复读,因其每次查询生成新快照而非锁定数据行,导致同一事务内多次读取可能返回其他事务已提交的修改结果。

在 JDBC 中实现“读已提交”(Read Committed)隔离级别,核心是通过 Connection.setTransactionIsolation() 显式设置,并配合事务控制来观察其对脏读和不可重复读的影响。它能防止脏读,但无法避免不可重复读——这正是 Read Committed 的标准行为。
设置 Read Committed 隔离级别
默认情况下,多数数据库(如 PostgreSQL、Oracle、SQL Server)的 JDBC 连接默认就是 Read Committed;MySQL 的 InnoDB 默认是可重复读(Repeatable Read),需手动调整。
- 调用
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)前,确保连接未处于自动提交模式:conn.setAutoCommit(false) - 该设置仅对当前 Connection 生效,且通常需在事务开始前设置(部分数据库如 MySQL 要求在事务外设置才生效)
- 设置后执行的 SQL 查询将遵循 Read Committed 规则:只能读到已提交的数据,不会读到未提交的修改
复现脏读被阻止(验证 Read Committed 有效性)
脏读指一个事务读取了另一个未提交事务的中间结果。在 Read Committed 下,这种读取会被阻塞或返回旧值(取决于数据库实现,如 PostgreSQL 会阻塞直到提交/回滚;MySQL InnoDB 使用 MVCC 快照,直接跳过未提交版本)。
- 事务 A 修改某行但不提交;事务 B 在 Read Committed 下执行 SELECT,查不到 A 的修改(即不会脏读)
- 若 A 回滚,B 始终看到原始值;若 A 提交,B 后续查询才能看到新值
- 注意:JDBC 中无需特殊代码,只要隔离级别正确,数据库引擎自动保证
复现不可重复读(Read Committed 的典型现象)
不可重复读指同一事务内两次读取同一行,结果不同(因其他事务中途提交了修改)。这是 Read Committed 允许的现象。
- 事务 A 第一次 SELECT 得到 name='Alice';事务 B 更新该行并 COMMIT;事务 A 再次 SELECT,得到 name='Bob'
- 在 JDBC 中只需两个并发 Connection,各自开启事务,按上述时序操作即可观察到
- 关键点:两次 SELECT 之间没有锁住该行(不像 Repeatable Read 会加间隙锁或使用快照),所以允许被更新覆盖
验证时的实用建议
为清晰观察现象,推荐使用支持明确事务控制和日志输出的数据库(如 PostgreSQL 或 H2 内存库),并配合线程 sleep 控制时序。
- 用
System.out.println打印每次查询结果和事务状态,避免依赖 IDE 控制台异步输出 - 每个 Connection 独立管理,不要共用 conn 对象;显式调用
commit()或rollback() - 测试完记得重置隔离级别或关闭连接,避免影响后续测试
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











