Как правильно использовать QSqlDatabase & QSqlQuery? - PullRequest
28 голосов
/ 06 октября 2011

Я запутался с руководством, должен ли я работать следующим образом:

{
 QSqlDatabase db = QSqlDatabase::addDatabase (...);
 QSqlQuery query (db);
 query.exec (...);
}

QSqlDatabase::removeDatabase (...);

Как указано в документе, query или db будет автоматически разобрано.Но так ли это эффективно?

Что ж, если я кеширую db внутри класса, как показано ниже:

class Dummy {
  Dummy() { 
    db = QSqlDatabase::addDatabase (...);
  }
  ~Dummy() {
    db.close();
  }

  bool run() {
    QSqlQuery query (db);
    bool retval = query.exec (...);
    blabla ...
  }

  private:
    QSqlDatabase db;
};

Иногда я вижу такие предупреждения, как:

QSqlDatabasePrivate::removeDatabase: connection 'BLABLA' is still in use, all queries will cease to work.

Даже если бы я не звонил run().

Ответы [ 3 ]

44 голосов
/ 07 октября 2011

Когда вы создаете объект QSqlDatabase с помощью addDatabase или когда вы вызываете removeDatabase, вы просто связываете или отсоединяете кортеж (драйвер, имя хоста: порт, имя базы данных, имя пользователя / пароль) к имени (или к имени соединения по умолчанию, если вы не указали имя соединения).
Драйвер SQL создается, но база данных открывается только при вызове QSqlDatabase::open.

Это имя соединения определяется для всего приложения. Поэтому, если вы вызываете addDatabase в каждом из объектов, которые его используют, вы изменяете все объекты QSqlDatabase, использующие одно и то же имя соединения, и аннулируете все запросы, которые были активны для них.

Первый пример кода, который вы привели, показывает, как правильно отсоединить имя соединения, гарантируя, что:

  • все QSqlQuery отсоединяются от QSqlDatabase перед закрытием базы данных, вызывая QSqlQuery::finish(), что происходит автоматически, когда объект QSqlQuery выходит из области видимости,
  • все QSqlDatabase с тем же именем соединения равны close() d при вызове QSqlDatabase::removeDatabase (close() также вызывается автоматически, когда объект QSqlDatabase выходит из области видимости).

Когда вы создаете базу данных QSqlDatabase, в зависимости от того, хотите ли вы, чтобы соединение оставалось открытым в течение времени жизни приложения (1) или просто при необходимости (2), вы можете:

  1. храните один экземпляр QSqlDatabase в одном классе (например, в вашем главном окне) и используйте его в других объектах, которые в этом нуждаются, передавая QSqlDatabase напрямую или просто имя соединения, которое вы перейдите к QSqlDatabase::database, чтобы получить экземпляр QSqlDatabase. QSqlDatabase::database использует QHash для извлечения QSqlDatabase из его имени, поэтому оно, вероятно, пренебрежимо медленнее, чем передача объекта QSqlDatabase непосредственно между объектами и функциями, и если вы используете соединение по умолчанию, вы даже не нужно что-то передавать куда угодно, просто позвоните QSqlDatabase::database() без параметров.

    // In an object that has the same lifetime as your application
    // (or as a global variable, since it has almost the same goal here)
    QSqlDatabase db;
    
    // In the constructor or initialization function of that object       
    db = QSqlDatabase::addDatabase("QSQLDRIVER", "connection-name"); 
    db.setHostname(...);
    // ...
    if(!this->db.open())  // open it and keep it opened
    {
        // Error handling...
    }
    
    // --------
    // Anywhere you need it, you can use the "global" db object 
    // or get the database connection from the connection name        
    QSqlDatabase db = QSqlDatabase::database("connection-name"); 
    QSqlQuery query(db);             
    
  2. сконфигурируйте QSqlDatabase один раз, откройте его, чтобы проверить правильность параметров, и отбросьте экземпляр. Имя соединения будет по-прежнему доступно в любом месте, но базу данных придется открыть заново:

    {
        // Allocated on the stack
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLDRIVER", "connection-name"); 
        db.setHostname(...);
        // ...
        if(!this->db.open()) // test the connection
        {
           // Error handling
        }
    // db is closed when it goes out of scope
    } 
    
    {
        // Same thing as for (1), but by default database() opens 
        // the connection if it isn't already opened 
        QSqlDatabase db = QSqlDatabase::database("connection-name"); 
        QSqlQuery query(db);
    
    // if there is no other connection open with that connection name,
    // the connection is closed when db goes out of scope
    } 
    

    В этом случае обратите внимание, что вам не следует явно закрывать базу данных, поскольку у вас может быть несколько объектов, использующих одно и то же соединение с базой данных с повторным входом (например, если функция A использует соединение и вызывает B, которые также используют Соединение. Если B закрывает соединение перед возвратом управления к A, соединение также будет закрыто для A, что, вероятно, плохо).

3 голосов
/ 06 октября 2011

QSqlDatabase и QSqlQuery - это легкие обертки вокруг конкретных реализаций, поэтому ваш первый пример в порядке. Если вы добавляете имя при добавлении соединения или используете базу данных по умолчанию, то просто запись «QSqlDatabase db (name)» дает вам объект базы данных с очень небольшими издержками.

removeDatabase эквивалентно закрытию файла (для sqlite) или соединения (для ODBC / MySql / Postgres), так что обычно это то, что вы делаете при завершении программы. Как говорится в предупреждении, вы должны убедиться, что все объекты базы данных и запросов, которые ссылаются на эту базу данных, уже уничтожены, иначе могут произойти плохие вещи.

0 голосов
/ 14 сентября 2013

Я считаю, что инструкции должны выполняться точно в указанном ниже порядке, иначе у вас возникнут проблемы с подключением к базе данных или запросом. Это работает в Qt5.

QSqlQueryModel *model = new QSqlQueryModel;
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(fileName);

if (db.isValid())
{
    db.open();
    if (db.isOpen())
    {
        QSqlQuery searchQuery(db);
        searchQuery.prepare("SELECT * FROM myTable");
        searchQuery.exec();
        if(searchQuery.isActive())
        {
            model->setQuery(searchQuery);
            sui->DBDisplay->setModel(model);
            db.close();
        } else {
            qDebug() << "query is not active";
        }
    } else {
        qDebug() << "DB is not open";
    }
} else {
    qDebug() << "DB is not valid";
}
...