根据请求改变事务隔离级别
Variable transaction isolation levels by request
我正在编写一个小的拍卖应用程序,确定地记录我的出价非常重要。毕竟,拍卖的最后几秒对买家来说是关键时刻,我不能冒险让他们同时出价和竞争条件。
当然,这就是事务隔离的目的。我可以将隔离级别设置为可序列化,一切就绪。
但是所有其他请求呢?如果人们正在查看个人资料或发送消息,则这些请求不需要任何接近这种事务隔离的地方。对于这些请求,读提交隔离级别是完全可以接受的。
我正在将我的事务级别设置为我休眠的一部分 属性 hibernate.connection.isolation
,但我真的希望能够根据请求执行 session.setTransactionIsolation(newIsolation)
之类的操作。
如果您正在使用 Spring,您可以使用这样的东西:
@Transactional(isolation = Isolation.SERIALIZABLE)
它适用于 JpaTransactionManager。如果您正在使用 JtaTransactionManager,则不会传播请求范围的事务隔离,因为这是默认的 JTA 行为。
因为 JTA 不支持事务范围的隔离级别,Spring 提供 IsolationLevelDataSourceRouter 来克服使用应用程序服务器 JTA 数据源时的这个缺点。
因为大多数数据源实现只能采用默认的事务隔离级别,我们可以有多个这样的数据源,每个都为特定事务隔离级别提供连接。
逻辑事务(例如@Transactional)隔离级别设置由 IsolationLevelDataSourceRouter 自省,因此连接获取请求被委托给特定的数据源实现,该实现可以为 JDBC 连接提供服务相同的事务隔离级别设置。
因此,即使在 JTA 环境中,事务隔离路由器也可以提供独立于供应商的解决方案,以在每个事务的基础上覆盖默认数据库隔离级别。
JavaEE不支持方法级事务隔离配置。
SERIALIZABLE 隔离级别将保护您免受不可重复读取和幻读的影响,甚至 SERIALIZABLE 也不能保护您免受跨多个请求逻辑事务的更新丢失。
乐观锁定6在使用分离的实体时扩展得更好(因为它们是在逻辑事务启动时加载的)。
Session session = getSession(dataSource, sessionFactory, Connection.TRANSACTION_SERIALIZABLE);
public Session getSession(DataSource dataSource, SessionFactory sessionFactory, int isolationLevel){
// Get connection from current dataSource and set new isolation
Connection connectionWithNewIsolation = dataSource.getConnection();
connectionWithNewIsolation.setTransactionIsolation(isolationLevel);
// Get session from current sessionFactory with the new isolation
Session session = sessionFactory.openSession(connectionWithNewIsolation);
// Hibernate 4.3
//SessionFactory.openStatelessSession(Connection connection)
// Hibernate 3.6
//SessionFactory.openSession(Connection connection)
//SessionFactory.openStatelessSession(Connection connection)
return session;
}
对于这种情况,我将在您的出价对象中使用 Optimistic lock...竞争条件仍然会发生,但是当事务尝试在您的域对象上提交更改(抛出如果读取的版本被另一个线程更新,则异常。
因此,对任何出价对象的任何更改几乎都是可序列化的(我说 "almost" 是因为为了可序列化,需要捕获失败的事务并以某种方式重试)。
如果为每个事务设置隔离级别失败,您始终可以在代码中手动序列化特定操作(同步、信号量等)。
但是请记住它不可扩展(单个 jvm,单个操作,很容易被代码的其他部分意外绕过)
我正在编写一个小的拍卖应用程序,确定地记录我的出价非常重要。毕竟,拍卖的最后几秒对买家来说是关键时刻,我不能冒险让他们同时出价和竞争条件。
当然,这就是事务隔离的目的。我可以将隔离级别设置为可序列化,一切就绪。
但是所有其他请求呢?如果人们正在查看个人资料或发送消息,则这些请求不需要任何接近这种事务隔离的地方。对于这些请求,读提交隔离级别是完全可以接受的。
我正在将我的事务级别设置为我休眠的一部分 属性 hibernate.connection.isolation
,但我真的希望能够根据请求执行 session.setTransactionIsolation(newIsolation)
之类的操作。
如果您正在使用 Spring,您可以使用这样的东西:
@Transactional(isolation = Isolation.SERIALIZABLE)
它适用于 JpaTransactionManager。如果您正在使用 JtaTransactionManager,则不会传播请求范围的事务隔离,因为这是默认的 JTA 行为。
因为 JTA 不支持事务范围的隔离级别,Spring 提供 IsolationLevelDataSourceRouter 来克服使用应用程序服务器 JTA 数据源时的这个缺点。
因为大多数数据源实现只能采用默认的事务隔离级别,我们可以有多个这样的数据源,每个都为特定事务隔离级别提供连接。
逻辑事务(例如@Transactional)隔离级别设置由 IsolationLevelDataSourceRouter 自省,因此连接获取请求被委托给特定的数据源实现,该实现可以为 JDBC 连接提供服务相同的事务隔离级别设置。
因此,即使在 JTA 环境中,事务隔离路由器也可以提供独立于供应商的解决方案,以在每个事务的基础上覆盖默认数据库隔离级别。
JavaEE不支持方法级事务隔离配置。
SERIALIZABLE 隔离级别将保护您免受不可重复读取和幻读的影响,甚至 SERIALIZABLE 也不能保护您免受跨多个请求逻辑事务的更新丢失。
乐观锁定6在使用分离的实体时扩展得更好(因为它们是在逻辑事务启动时加载的)。
Session session = getSession(dataSource, sessionFactory, Connection.TRANSACTION_SERIALIZABLE);
public Session getSession(DataSource dataSource, SessionFactory sessionFactory, int isolationLevel){
// Get connection from current dataSource and set new isolation
Connection connectionWithNewIsolation = dataSource.getConnection();
connectionWithNewIsolation.setTransactionIsolation(isolationLevel);
// Get session from current sessionFactory with the new isolation
Session session = sessionFactory.openSession(connectionWithNewIsolation);
// Hibernate 4.3
//SessionFactory.openStatelessSession(Connection connection)
// Hibernate 3.6
//SessionFactory.openSession(Connection connection)
//SessionFactory.openStatelessSession(Connection connection)
return session;
}
对于这种情况,我将在您的出价对象中使用 Optimistic lock...竞争条件仍然会发生,但是当事务尝试在您的域对象上提交更改(抛出如果读取的版本被另一个线程更新,则异常。
因此,对任何出价对象的任何更改几乎都是可序列化的(我说 "almost" 是因为为了可序列化,需要捕获失败的事务并以某种方式重试)。
如果为每个事务设置隔离级别失败,您始终可以在代码中手动序列化特定操作(同步、信号量等)。 但是请记住它不可扩展(单个 jvm,单个操作,很容易被代码的其他部分意外绕过)