3

如何使用 Squeryl 设置事务隔离级别?

例如,现在我正在使用 Postgresql,并且需要对特定的单个事务进行可序列化的隔离。我将普通的 Squeryl 和 Squeryl-Record 与 Lift Web 框架一起使用。

其他人当然可能需要针对整个会话(而不是单个事务)的其他数据库的其他隔离级别,所以一般的答案是可取的。

更新:

我最终得到了 Dave Whittaker 代码的修改版本:

def transactionWith[T](isolation: Int)(block: => T): T =
  transaction {
    val connection = Session.currentSession.connection
    connection.rollback // isolation cannot be changed in the middle of a tx
    connection.setTransactionIsolation(isolation)
    block
  }

问题是,如果事务已经启动,则无法更改隔离级别。对我来说就是这种情况,如果没有回滚,我会得到:

org.postgresql.util.PSQLException:无法在事务中间更改事务隔离级别。

只要我使用 transaction{} 而不是 inTransaction{},我认为立即回滚应该没有害处。

隔离级别应在事务提交或回滚后重置,但在连接返回到连接池之前。我不确定如何做到这一点。但在我的情况下,c3p0 连接池似乎重置了隔离级别,并且每个事务{}都以默认隔离级别开始,即使我自己从未清理过它们。

我不太满意的是发生冲突时的例外情况。我想专门捕获这样的异常并重试事务。但这只是一个通用的运行时异常:

java.lang.RuntimeException:执行语句时出现异常:错误:由于并发更新而无法序列化访问

它包装了另一个异常,不幸的是它也是通用的(org.postgresql.util.PSQLException)。

不完美,但在 Squeryl 希望获得对事务隔离的支持之前,它可以完成这项工作。我将上述代码与 Squeryl 0.9.4 一起使用。

4

2 回答 2

3

现在这将是一个有点手动的过程。如果您在整个会话中都需要它,那么我想您可以简单地在 SessionFactory 中设置适当的级别,即

SessionFactory.concreteFactory = Some(()=> {
  val connection = java.sql.DriverManager.getConnection("...")
  connection.setTransactionIsolation(...)
  Session.create(connection, new PostgreSqlAdapter)
 })

对于单笔交易,这将更加困难。您可以使用 Session.currentSession 或 Session.currentSessionOption 访问当前会话,并且必须在事务发生之前设置隔离级别,然后再将其设置回来。当然,创建自己的函数来做到这一点并不难:

def transactionWith(isolation: Int)(block: => T): T = {
  trasaction{
    val connection = Session.currentSession.connection
    val oldIsolation = connection.getTransactionIsolation()
    connection.setTransactionIsolation(isolation)
    try {
      block
    } finally {
      connection.setTransactionIsolation(oldIsolation)
    }
  }
}

然后你会像使用它一样

transactionWith(Connection.TRANSACTION_SERIALIZABLE){
   from(blablabla)(......)
}

我认为这可行,但是a)我不完全确定何时应该设置隔离级别,我假设在执行任何其他语句之前在当前事务中设置它会起作用,并且b)我没有尝试过编译上述内容,因此可能存在语法错误。无论如何,我认为它会给你一个大致的想法。

于 2011-11-25T23:03:28.967 回答
1

关于异常:org.postgresql.util.PSQLExceptionextends java.sql.SQLException,它有一个getSQLState()方法。像这样的序列化失败导致的异常"40001"将从这个方法返回。

于 2012-04-03T23:43:50.283 回答