H2数据库事务隔离级别设置与查询:是否存在显示异常问题?
在H2 2.2.220版本中
假设从H2控制台设置事务隔离级别:
根据H2教程,存在H2控制台专用语法用于设置或查询事务隔离级别:
@transaction_isolation;:(不带参数)显示事务隔离级别。@transaction_isolation <levelAsInt>;:修改事务隔离级别。
此处文档需更新,因为未列出级别6(snapshot),实际支持的级别如下:
- 1: read_uncommitted
- 2: read_committed
- 4: repeatable_read
- 6: snapshot
- 8: serializable(可能仅部分支持,暂不确定)
但此处的「事务隔离级别」指代不明:是当前会话级别?全局级别?还是默认级别?继续测试:
查询事务隔离级别:
@transaction_isolation; Transaction Isolation: 2 1: read_uncommitted 2: read_committed 4: repeatable_read 6: snapshot 8: serializable?: @transaction_isolation
显示当前处于read_committed级别...
隔离级别本质应为会话的属性。
H2的信息架构可查询所有当前会话的隔离级别:
SELECT SESSION_ID, ISOLATION_LEVEL, SESSION_ID = SESSION_ID() AS "IS_MY_SESSION" FROM INFORMATION_SCHEMA.SESSIONS;

执行@命令后,所有会话的隔离级别均被修改(注:仅控制台一个会话处于活跃状态):
@transaction_isolation 8; SELECT SESSION_ID, ISOLATION_LEVEL, SESSION_ID = SESSION_ID() AS "IS_MY_SESSION" FROM INFORMATION_SCHEMA.SESSIONS;

这似乎合理。
但程序也可通过SQL命令SET SESSION CHARACTERISTICS设置事务隔离级别
修改当前会话的事务隔离级别。实际支持的隔离级别取决于数据库引擎。
可接受的值(定义于H2 javadoc):
READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLESNAPSHOT(可正常使用,但未在手册中列出)
执行以下命令:
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT SESSION_ID, ISOLATION_LEVEL, SESSION_ID = SESSION_ID() AS "IS_MY_SESSION" FROM INFORMATION_SCHEMA.SESSIONS;
预期仅当前会话受影响,但实际观察到所有会话均被修改:
该如何解释此现象?
更令人困惑的是:
通过Spring JDBC应用直接查询和设置事务隔离级别(以下为测试代码,正确方式是使用@Transactional注解并设置其isolation属性),数据库显示会话的事务隔离级别已调整,例如INFORMATION_SCHEMA.SESSIONS显示为REPEATABLE_READ。但此时控制台仍显示所有现有会话的隔离级别为通过@命令设置的统一值,且Java程序运行后会话数量增多。
这是显示问题?还是会话立即恢复为@命令设置的默认值?
尚未测试隔离级别是否真正生效,后续将验证!
记录类:
public record AboutSession(int sessionId, IsolationLevel isolationLevel) {}
查询事务隔离级别(IsolationLevel枚举代码省略):
public static AboutSession getAboutSession (@NotNull JdbcTemplate jdbcTemplate) { return (jdbcTemplate.query("SELECT SESSION_ID, ISOLATION_LEVEL FROM " + " INFORMATION_SCHEMA.SESSIONS WHERE SESSION_ID = SESSION_ID()", (ResultSet row, int rowNum) -> new AboutSession( row.getInt(1), IsolationLevel.fromString(row.getString(2)))) ).get(0); }
设置事务隔离级别:
public static void setSessionIsolationLevel( @NotNull JdbcTemplate jdbcTemplate, @NotNull IsolationLevel level) { jdbcTemplate.execute("SET SESSION CHARACTERISTICS AS " + " TRANSACTION ISOLATION LEVEL " + level.toSql(level)); }
不依赖H2代码的IsolationLevel枚举:
public enum IsolationLevel { READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE, SNAPSHOT; public static IsolationLevel fromString(@NotNull String x) { String xx = x.toUpperCase(); switch (xx) { case "READ UNCOMMITTED" -> { return READ_UNCOMMITTED; } case "READ COMMITTED" -> { return READ_COMMITTED; } case "REPEATABLE READ" -> { return REPEATABLE_READ; } case "SERIALIZABLE" -> { return SERIALIZABLE; } case "SNAPSHOT" -> { return SNAPSHOT; } default -> throw new IllegalArgumentException("Cannot map string " + x + " to a valid " + IsolationLevel.class.getName()); } } public static String toSql (@NotNull IsolationLevel isolationLevel) { return switch (isolationLevel) { case READ_UNCOMMITTED -> "READ UNCOMMITTED"; case READ_COMMITTED -> "READ COMMITTED"; case REPEATABLE_READ -> "REPEATABLE READ"; case SERIALIZABLE -> "SERIALIZABLE"; case SNAPSHOT -> "SNAPSHOT"; }; } }
内容的提问来源于stack exchange,提问作者David Tonhofer

