web-dev-qa-db-fra.com

Quelle est la manière correcte de QSqlDatabase & QSqlQuery?

J'ai été confondu avec le manuel, devrais-je travailler comme ceci:

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

QSqlDatabase::removeDatabase (...);

Comme le souligne le document, query ou db sera automatiquement déconstruit . Mais est-ce efficace?

Eh bien, si je cache db dans une classe, comme suit:

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

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

  private:
    QSqlDatabase db;
};

Parfois, je pouvais voir des avertissements comme: 

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

Même si je n’ai pas appelé run().

27
daisy

Lorsque vous créez un objet QSqlDatabase avec addDatabase ou lorsque vous appelez removeDatabase, vous associez ou dissociez simplement un Tuple (pilote, nom d'hôte: port, nom de la base de données, nom d'utilisateur/mot de passe) à un nom (ou au nom de connexion par défaut si vous ne spécifiez pas un nom de connexion). 
Le pilote SQL est instancié, mais la base de données ne sera ouverte que lorsque vous appelez QSqlDatabase::open.

Ce nom de connexion est défini à l'échelle de l'application. Ainsi, si vous appelez addDatabase dans chacun des objets qui l'utilisent, vous modifiez tous les objets QSqlDatabase qui utilisent le même nom de connexion et invalide toutes les requêtes qui étaient actives sur eux. 

Le premier exemple de code que vous avez cité montre comment dissocier correctement le nom de la connexion en veillant à ce que:

  • toutes les QSqlQuery sont détachées de la QSqlDatabase avant la fermeture de la base de données en appelant QSqlQuery::finish(), qui est automatique lorsque l'objet QSqlQuery sort de la portée,
  • tous les QSqlDatabase avec le même nom de connexion sont close()d lorsque vous appelez QSqlDatabase::removeDatabase (close() est également appelé automatiquement lorsque l'objet QSqlDatabase sort de la portée).

Lorsque vous créez la base de données QSql, selon que vous souhaitez que la connexion reste ouverte pendant la durée de vie de l'application (1) ou juste au besoin (2), vous pouvez:

  1. conservez une seule instance QSqlDatabase dans une seule classe (par exemple, dans votre fenêtre principale) et utilisez-la dans les autres objets qui en ont besoin, soit en transmettant directement QSqlDatabase, soit simplement le nom de la connexion que vous transmettez à QSqlDatabase::database pour récupérer l'instance QSqlDatabase. QSqlDatabase::database utilise QHash pour extraire une QSqlDatabase de son nom. Il est donc probablement beaucoup plus lent que de transmettre l'objet QSqlDatabase directement entre des objets et des fonctions. Si vous utilisez la connexion par défaut, vous n'avez même rien à transmettre QSqlDatabase::database() sans aucun paramètre.

    // 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. configurez une fois la QSqlDatabase, ouvrez-la pour vérifier que les paramètres sont corrects et abandonnez l'instance. Le nom de la connexion sera toujours accessible n'importe où, mais la base de données devra être rouverte:

    {
        // 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
    } 
    

    Dans ce cas, notez que vous ne devez pas fermer explicitement la base de données, car vous pouvez avoir plusieurs objets utilisant la même connexion à la base de données de manière réentrante (par exemple, si une fonction A utilise la connexion et les appels B qui utilisent également la connexion. Si B ferme la connexion avant de rendre le contrôle à A, la connexion sera également fermée pour A, ce qui est probablement une mauvaise chose).

42
alexisdm

QSqlDatabase et QSqlQuery sont des enveloppeurs légers entourant des implémentations concrètes. Votre premier exemple est donc parfait. Si vous fournissez un nom lors de l'ajout de la connexion ou si vous utilisez la base de données par défaut, la simple écriture de 'QSqlDatabase db (name)' vous donne l'objet de base de données avec très peu de charge.

removeDatabase équivaut à fermer le fichier (pour sqlite) ou la connexion (pour ODBC/MySql/Postgres), c'est donc généralement quelque chose que vous feriez à la fin du programme. Comme le dit l'avertissement, vous devez vous assurer que tous les objets de la base de données et de la requête faisant référence à cette base de données ont déjà été détruits ou que de mauvaises choses peuvent arriver.

3
James Turner

Je trouve que les instructions doivent être exécutées exactement dans l'ordre indiqué ci-dessous, sinon vous avez des problèmes, que ce soit avec la connexion à la base de données ou la requête. Cela fonctionne dans 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";
}
0
Pescolly