Транзакция SQLite не фиксируется - PullRequest
1 голос
/ 18 ноября 2009

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

Я начинаю транзакцию, читаю строки и, пока перебираю набор записей, создаю строку операторов обновления. После завершения чтения набора записей я закрываю набор записей и запускаю обновления. На этом этапе я фиксирую транзакцию, однако в базе данных не выполняется ни одного обновления.

 private static SQLiteConnection OpenNewConnection()
        {

        try
        {
            SQLiteConnection conn = new SQLiteConnection();
            conn.ConnectionString = ConnectionString;//System.Configuration.ConfigurationManager.AppSettings["ConnectionString"];
            conn.Open();
            return conn;
        }               
        catch (SQLiteException e)
        {
            LogEvent("Exception raised when opening connection to [" + ConnectionString + "].  Exception Message " + e.Message);
            throw e;
        }
    }

    SQLiteConnection conn = OpenNewConnection();
            SQLiteCommand command = new SQLiteCommand(conn);
            SQLiteTransaction transaction = conn.BeginTransaction();
// Also fails           transaction = conn.BeginTransaction();
            transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
            command.CommandType = CommandType.Text;
            command.Transaction = transaction;
            command.Connection = conn;
            try
            {
                string sql = "select * From X Where Y;";
                command.CommandText = sql;
                SQLiteDataReader ranges;

                ranges = command.ExecuteReader();
                sql = string.Empty;
                ArrayList ret = new ArrayList();
                while (MemberVariable > 0 && ranges.Read())
                {
                    // Domain stuff

                    sql += "Update X Set Z = 'foo' Where Y;";
                }
                ranges.Close();
                command.CommandText = sql;
                command.ExecuteNonQuery();
                                // UPDATES NOT BEING APPLIED
                transaction.Commit();
                return ret;

            }
            catch (Exception ex)
            {
                transaction.Rollback();
                throw;
            }
            finally
            {
                transaction.Dispose();
                command.Dispose();
                conn.Close();
            }

            return null;

Если я удалю транзакцию, все будет работать как положено. «Материал домена» специфичен для домена и, кроме чтения значений из набора записей, не обращается к базе данных. Я забыл шаг?

Ответы [ 2 ]

4 голосов
/ 18 ноября 2009

Когда вы ставите точку останова на вашей транзакции. Строка коммита () вы видите, что ее ударили?

Окончательный ответ:

Блокировка SQLite не работает так, как вы предполагаете, смотрите http://www.sqlite.org/lockingv3.html. Учитывая это, я думаю, что у вас возникла проблема с ограничением объема транзакций, которую можно легко решить, реорганизовав код следующим образом:

string selectSql = "select * From X Where Y;";      
using(var conn = OpenNewConnection()){
    StringBuilder updateBuilder = new StringBuilder();

    using(var cmd = new SQLiteCommand(selectSql, conn))
    using(var ranges = cmd.ExecuteReader()) {
        while(MemberVariable > 0 && ranges.Read()) {
            updateBuilder.Append("Update X Set Z = 'foo' Where Y;");
        }
    }

    using(var trans = conn.BeginTransaction())
    using(var updateCmd = new SQLiteCommand(updateBuilder.ToString(), conn, trans) {
        cmd.ExecuteNonQuery();
        trans.Commit();
    }
}   
2 голосов
/ 20 марта 2011

Дополнительные примечания относительно некоторых комментариев в этом посте / ответа о транзакциях в SQLite. Они применимы к SQLite 3.x с использованием журналирования и могут относиться или не относиться к различным конфигурациям - WAL немного отличается, но я не знаком с ним. См. блокировка в SQLite для получения полной информации.

Все транзакции в SQLite SERIALIZABLE (см. Прагму read_uncommitted для одного небольшого исключения). A new чтение не будет блокировать / терпеть неудачу , если процесс записи не запущен (удерживается блокировка EXCLUSIVE / PENDING), и запись не начнется, пока все ожидающие чтения не будут завершены и он может получить ИСКЛЮЧИТЕЛЬНУЮ блокировку (это не так для WAL , но изоляция транзакции остается прежней).

То есть вся приведенная выше последовательность не будет атомарной в коде , и последовательность может быть прочитана (A) -> прочитана (B) -> записана (A) -> прочитана (B), где A и B представляют разные соединения (представьте в разных потоках). При обоих чтениях (B) данные по-прежнему непротиворечивы, даже если между ними была запись.

Чтобы сделать последовательность самого кода атомарной, требуется lock или аналогичный механизм синхронизации. В качестве альтернативы, блокировка / синхронизация может быть создана с помощью самого SQLite с использованием прагмы locking_mode «эксклюзив». Однако даже если приведенный выше код не является атомарным , данные будут соответствовать сериализуемому контракту SQL (исключая серьезную ошибку; -)

Счастливое кодирование


См. Блокировка в SQLite , Прагмы SQLite и Атомная фиксация в SQLite

...