Добрый день!
Хочу поделится описанием неочевидной проблемы, с которой я столкнулся при программировании запросов к базе данных на 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...
В-общем, надеюсь, что это кому-нибудь сэкономит время! Удачного программирования!