Подтвердить что ты не робот

Каков правильный способ QSqlDatabase & QSqlQuery?

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

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

QSqlDatabase::removeDatabase (...);

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

Ну, если я cache 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().

4b9b3361

Ответ 1

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

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

В приведенном выше примере кода показано, как правильно отключить имя соединения, указав, что:

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

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

  • сохранить один экземпляр 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);             
    
  • настройте 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, что, вероятно, является плохим).

Ответ 2

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

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

Ответ 3

Я обнаружил, что инструкции должны выполняться точно в порядке, указанном ниже, или у вас есть проблемы с подключением к базе данных или запросом. Это работает в 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";
}