在 H2 2.2.220
假设我从H2控制台设置事务隔离级别:
根据H2教程,有一个特殊的H2控制台语法来设置或查询事务隔离级别:
@transaction_isolation;
:显示(不带参数)事务隔离级别。@transaction_isolation <levelAsInt>;
更改事务隔离级别。此处的文档需要进行一些更新,因为它没有列出级别 6“快照”,但级别如下:
然而,尚不清楚“事务隔离级别”在这里指的是什么。是当前会话中的一个吗?单一的全球性的吗?默认的?但我们继续吧...
查询“事务隔离级别”则:
@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;
有人可能会说,这是有道理的。
SET SESSION CHARACTERISTICS
更改当前会话的事务隔离级别。隔离级别的实际支持取决于数据库引擎。
具有可接受的值(如H2 javadoc中所定义):
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
SNAPSHOT
(也可以,但手册中没有)如果现在运行
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
。但在此过程中,控制台still报告事务隔离级别统一为所有现有会话上使用@
命令设置的任何级别,一旦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));
}
IsolationLevel
枚举,不依赖于H2代码:
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";
};
}
}
是的,
INFORMATION_SCHEMA
有一个错误。
所有行中返回当前会话的隔离级别,而不是这些行中描述的会话的实际隔离级别。
我针对这个bug补了一个新问题: https://github.com/h2database/h2database/issues/3868