如何使用Squeryl设置事务隔离级别?
例如,现在我正在使用Postgresql,需要对特定的单个事务进行可序列化的隔离。我使用普通的Squeryl和Squeryl-记录与电梯网络框架。
当然,对于整个会话(而不是单个事务)的其他数据库,其他数据库可能需要其他隔离级别,因此更可取的是一般的答案。
更新:
最后,我得到了代码的修改版本:
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:执行语句时的异常:错误:由于并发更新,无法序列化访问
它封装了另一个异常,不幸的是,它也是泛型的(org.postgresql.util.PSQLException)。
这并不完美,但在Squeryl有望获得事务隔离支持之前,它就能完成这项工作。我使用了上面的代码与Squeryl 0.9.4。
发布于 2011-11-25 15:03:28
现在,这将是一个有点手动的过程。如果整个会话都需要它,那么我想您可以简单地在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)我没有尝试编译上面的内容,因此可能会出现语法错误。不管怎么说,我想这会给你一个大致的想法。
发布于 2012-04-03 15:43:50
关于异常:org.postgresql.util.PSQLException
扩展了java.sql.SQLException
,它有一个getSQLState()
方法。这种序列化失败导致的异常将从此方法返回"40001"
。
https://stackoverflow.com/questions/8272848
复制相似问题