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()
.
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:
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,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:
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);
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).
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.
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";
}