Установка уровня изоляции транзакции в Squeryl - PullRequest
3 голосов
/ 25 ноября 2011

Как установить уровень изоляции транзакции с помощью Squeryl?

Например, сейчас я использую Postgresql и мне нужна сериализуемая изоляция для отдельных транзакций. Я использую как простой Squeryl, так и Squeryl-Record с веб-фреймворком Lift.

Другим, разумеется, могут понадобиться другие уровни изоляции для других баз данных для целых сеансов (а не отдельных транзакций), поэтому предпочтительны общие ответы.

Обновление:

Я закончил с этой модифицированной версией кода Дэйва Уиттекера:

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.

Ответы [ 2 ]

3 голосов
/ 26 ноября 2011

Прямо сейчас это будет несколько ручной процесс.Если вам это нужно для всего сеанса, тогда, я полагаю, вы могли бы просто установить соответствующий уровень в вашей 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)(......)
}

Я думаю, что это сработает, ноа) я не совсем уверен, когда должен быть установлен уровень изоляции, я предполагаю, что установка его в текущей транзакции перед выполнением любых других операторов будет работать, и б) я не пытался скомпилировать выше, так чтомогут быть синтаксические ошибки.Во всяком случае, я думаю, что это даст вам общее представление.

1 голос
/ 04 апреля 2012

Относительно исключения: org.postgresql.util.PSQLException extends java.sql.SQLException, который имеет метод getSQLState(). Исключения, вызванные такими ошибками сериализации, возвращают "40001" из этого метода.

...