Russian Qt Forum
Июнь 16, 2024, 10:31 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Странное поведение QSqlQuery и ошибка Driver Not Loaded  (Прочитано 6322 раз)
SadSido
Гость
« : Декабрь 08, 2008, 13:00 »

Добрый день!

Хочу поделится описанием неочевидной проблемы, с которой я столкнулся при программировании запросов к базе данных на SQLite (от типа базы данных проблема не зависит). В коде создаётся некий модуль, инкапсулирующий работу с БД. Создаётся сама база и несколько наиболее употребляемых запросов SQL:

Код:
class CDatabaseModule 
{
private:
      QSqlDatabase mDatabase;
      QSqlQuery      mBeginQuery;
      QSqlQuery      mCommitQuery;
public:
      // Открытый конструктор
      CDatabaseModule( /*параметры соединения*/ )
      {
            mDatabase = QSqlDatabase::addDatabase( /*драйвер*/ );
            mDatabase.setDatabaseName( /*имя БД*/ );
            mDatabase.setUserName( /*логин*/ );
            mDatabase.setPassword( /*пароль*/ );

            // Открываем БД (с проверкой корректности)
            mDatabase.open();

            // Кешируем часто употребляемые запросы
            mBeginQuery.prepare("BEGIN TRANSACTION;");
            mCommitQuery.prepare("COMMIT TRANSACTION;");
      }
      // Далее методы обработки данных
}

Этот код содержит фундаментальную ошибку, поведение которой выражается в том, что все SQL-запросы к этой БД будут провалены. То есть, для любого объекта QSqlQuery к этой БД метод exec() вернёт false. Будет получен объект QSqlError с текстом driverText() = "Driver not loaded" и databaseText() = "Driver not loaded". На отлов этой ошибки может уйти много времени, но всегда приятнее изучать чужие грабли, чем наступать на свои. Итак:

Объекты mBeginQuery и mCommitQuery создаются на стеке, а не в динамической памяти, и, поскольку в коде конструктора нет никаких указаний по поводу их инициализации, компилятор вызывает для них конструкторы по умолчанию. Класс QSqlQuery имеет конструктор без параметров: QSqlQuery(const QString& iQuery = QString(), QSqlDatabase iDB = 0). Таким образом, объекты mBeginQuery и mCommitQuery инициализируются ссылкой на базу данных до того, как было установлено соединение с базой данных. Преодолевается ошибка следующим образом:

Код:
// ... 
private:
      QSqlQuery * mBeginQuery;
      QSqlQuery * mCommitQuery;
// ...
// в конструкторе. Теперь объекты создаются вовремя!
      mBeginQuery = new QSqlQuery();
      mBeginQuery->prepare("BEGIN TRANSACTION;");
      mCommitQuery = new QSqlQuery();
      mCommitQuery->prepare("COMMIT TRANSACTION;");
// ...
// И не забудьте про деструктор!!
      delete mBeginQuery;
      delete mCommitQuery;

Из всего этого мне осталось непонятным лишь только, почему ВСЕ запросы проваливались? Даже те, что были созданы после инициализации базы данных... Полагаю тут проявляется какой-нибудь побочный эффект работы с глобальными переменными QSqlDatabase...

В-общем, надеюсь, что это кому-нибудь сэкономит время! Удачного программирования!
Записан
ритт
Гость
« Ответ #1 : Декабрь 08, 2008, 13:13 »

Цитировать
bool QSqlDatabase::transaction ()

Begins a transaction on the database if the driver supports transactions. Returns true if the operation succeeded. Otherwise it returns false.

Цитировать
bool QSqlDatabase::commit ()

Commits a transaction to the database if the driver supports transactions and a transaction() has been started. Returns true if the operation succeeded. Otherwise it returns false.

Note: For some databases, the commit will fail and return false if there is an active query using the database for a SELECT. Make the query inactive before doing the commit.

Call lastError() to get information about errors.

Цитировать
bool QSqlDatabase::rollback ()

Rolls back a transaction on the database, if the driver supports transactions and a transaction() has been started. Returns true if the operation succeeded. Otherwise it returns false.

Note: For some databases, the rollback will fail and return false if there is an active query using the database for a SELECT. Make the query inactive before doing the rollback.

Call lastError() to get information about errors.
Записан
Rcus
Гость
« Ответ #2 : Декабрь 08, 2008, 13:37 »

Пока соединение с базой не установлено запросы нормально не инициализируются. Можно просто добавить после открытия соединения
Код
C++ (Qt)
queryVar = QSqlQuery(databaseVar);
Записан
SadSido
Гость
« Ответ #3 : Декабрь 08, 2008, 15:26 »

> Константин
Спасибо! Database::transaction() и Database::commit()... Уже несколько раз убеждался, что пытаюсь изобрести то, что уже есть в Qt4. Но пользователь может захотеть кешировать произвольные запросы, например SELECT.

>Rcus
Вы уверены? По-моему, этот номер не пройдёт Улыбающийся Обращаю внимание, что в исходном коде проваливались ВСЕ запросы к БД, а не только "BEGIN" и "COMMIT"... Как мне кажется, объявлять переменные QSqlQuery на стеке  в рассматриваемом примере просто нельзя .
Записан
Rcus
Гость
« Ответ #4 : Декабрь 08, 2008, 17:08 »

Уверен, потому что и на вашем примере проверил, и много раз до этого так делал.
Попробуйте после
Код
C++ (Qt)
mDatabase.open();
вставить
Код
C++ (Qt)
mBeginQuery = QSqlQuery(mDatabase);
mCommitQuery = QSqlQuery(mDatabase);
Записан
ритт
Гость
« Ответ #5 : Декабрь 08, 2008, 18:28 »

Спасибо! Database::transaction() и Database::commit()... Уже несколько раз убеждался, что пытаюсь изобрести то, что уже есть в Qt4. Но пользователь может захотеть кешировать произвольные запросы, например SELECT.
пожалуйста. у тебя такой же точно ассистант и ты точно так же мог копнуть доки по интересующему вопросу.
кешируй на здоровье. только не забудь перед закрытием и удалением базы отключить "кешируемый" запрос (queryVar = QSqlQuery() или queryVar.finish())...

Обращаю внимание, что в исходном коде проваливались ВСЕ запросы к БД, а не только "BEGIN" и "COMMIT"... Как мне кажется, объявлять переменные QSqlQuery на стеке  в рассматриваемом примере просто нельзя .
можно. а запросы у тебя проваливались (если действительно _все_), скорее всего, из-за сбоя инициализации - попробовал бы клонироваь коннект и проверить запросы на клоне.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.275 секунд. Запросов: 20.