30

我对手册感到困惑,我应该这样工作:

{
 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()

4

3 回答 3

46

当您使用或调用创建QSqlDatabase对象时,您只是将元组(驱动程序、主机名:端口、数据库名称、用户名/密码)与名称(或默认连接名称,如果您不指定连接名称)。SQL驱动被实例化了,但是数据库只有在调用时才会打开。addDatabaseremoveDatabase
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中检索 a ,因此它可能比直接在对象和函数之间传递对象慢得可以忽略不计,并且如果您使用默认连接,您甚至不必在任何地方传递任何东西,只需在没有任何参数的情况下调用。QSqlDatabaseQSqlDatabaseQSqlDatabase::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 关闭,这可能是一件坏事)。

于 2011-10-07T15:38:15.640 回答
4

QSqlDatabase 和 QSqlQuery 是围绕具体实现的轻量级包装器,因此您的第一个示例很好。如果您在添加连接时提供名称,或者使用默认数据库,那么只需编写“QSqlDatabase db(name)”即可为您提供很少开销的数据库对象。

removeDatabase 相当于关闭文件(对于 sqlite)或连接(对于 ODBC/MySql/Postgres),所以这通常是您在程序终止时会做的事情。正如警告所说,您必须确保所有引用该数据库的数据库和查询对象都已被销毁,否则可能会发生坏事。

于 2011-10-06T18:05:35.330 回答
0

我发现指令必须完全按照下面的顺序运行,否则您会遇到问题,无论是数据库连接还是查询。这适用于 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";
}
于 2013-09-14T06:19:09.213 回答