如何使用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:无法在事务中间更改事务隔离级别。
只要我使用事务{}而非inTransaction {}我认为立即回滚应该没有坏处。
隔离级别应在事务{}提交或回滚后重置,但在连接返回到连接池之前。我不知道如何做到这一点。但在我的情况下,c3p0连接池似乎重置了隔离级别,并且每个事务{}都以默认隔离级别开始,即使我从未自己清理它们。
我不满意的是发生冲突时的例外情况。我想特别捕获这样的异常并重试该事务。但它只是一个通用的运行时异常:
java.lang.RuntimeException:执行语句时出现异常:ERROR:由于并发更新而无法序列化访问
它包装了另一个异常,遗憾的是它也是通用的(org.postgresql.util.PSQLException)。
并不完美,但在Squeryl希望获得对事务隔离的支持之前,它才能完成任务。我使用上面的代码与Squeryl 0.9.4。
答案 0 :(得分: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)我还没有尝试编译以上内容,因此可能存在语法错误。无论如何,我认为它会给你一般的想法。
答案 1 :(得分:1)
关于例外:org.postgresql.util.PSQLException
扩展java.sql.SQLException
,其中getSQLState()
方法。像这样的序列化失败导致的异常将从此方法返回"40001"
。