example - ¿Cuál es la forma correcta de QSqlDatabase y QSqlQuery?
qt sql connection (3)
Cuando crea un objeto QSqlDatabase
con addDatabase
o cuando llama a removeDatabase
, simplemente está asociando o desasociando una tupla (controlador, nombre de host: puerto, nombre de base de datos, nombre de usuario / contraseña) a un nombre (o al nombre de conexión predeterminado si no lo hace) t especificar un nombre de conexión).
El controlador SQL se instancia, pero la base de datos solo se abrirá cuando llame a QSqlDatabase::open
.
Ese nombre de conexión se define en toda la aplicación. Por lo tanto, si llama a addDatabase
en cada uno de los objetos que lo usan, está cambiando todos los objetos QSqlDatabase
que usan el mismo nombre de conexión e invalidando todas las consultas que estuvieron activas en ellos.
El primer ejemplo de código que citó muestra cómo desasociar correctamente el nombre de la conexión, asegurándose de que:
- todos los
QSqlQuery
se separan deQSqlDatabase
antes de cerrar la base de datos llamando aQSqlQuery::finish()
, que es automático cuando el objetoQSqlQuery
queda fuera del alcance, - todas las
QSqlDatabase
con el mismo nombre de conexión sonclose()
d cuando llama aQSqlDatabase::removeDatabase
(close()
también se llama automáticamente cuando el objetoQSqlDatabase
sale del alcance).
Cuando crea la base QSqlDatabase, dependiendo de si desea que la conexión permanezca abierta durante la vida útil de la aplicación (1) o cuando sea necesario (2), puede:
mantenga una única instancia de
QSqlDatabase
en una sola clase (por ejemplo, en su ventana principal) y úsela en otros objetos que la necesiten pasando la base de datosQSqlDatabase
directamente o solo el nombre de conexión que pasa aQSqlDatabase::database
para obtener laQSqlDatabase::database
deQSqlDatabase::database
QSqlDatabase
instancia de vuelta.QSqlDatabase::database
usaQHash
para recuperar unaQSqlDatabase
de su nombre, por lo que es probablemente más lento que pasar el objetoQSqlDatabase
directamente entre objetos y funciones, y si usas la conexión predeterminada, ni siquiera tienes que pasar nada por ningún lado , simplemente llame aQSqlDatabase::database()
sin ningún parámetro.// 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);
configure
QSqlDatabase
una vez, ábralo para probar que los parámetros son correctos y elimine la instancia. El nombre de la conexión seguirá estando accesible en cualquier lugar, pero la base de datos tendrá que volver a abrirse:{ // 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 }
En ese caso, tenga en cuenta que no debe cerrar la base de datos explícitamente, porque puede tener múltiples objetos usando la misma conexión de base de datos en una forma reentrante (por ejemplo, si una función A usa la conexión y llama a B que también usa la conexión. Si B cierra la conexión antes de devolver el control a A, la conexión también se cerrará para A, lo que probablemente sea algo malo).
Me confundí con el manual, debería funcionar así:
{
QSqlDatabase db = QSqlDatabase::addDatabase (...);
QSqlQuery query (db);
query.exec (...);
}
QSqlDatabase::removeDatabase (...);
Como se señala en el documento, query
o db
se deconstruirán automáticamente. Pero es eso eficiente?
Bueno, si guardo db
dentro de una clase, como el siguiente:
class Dummy {
Dummy() {
db = QSqlDatabase::addDatabase (...);
}
~Dummy() {
db.close();
}
bool run() {
QSqlQuery query (db);
bool retval = query.exec (...);
blabla ...
}
private:
QSqlDatabase db;
};
A veces puedo ver advertencias como:
QSqlDatabasePrivate::removeDatabase: connection ''BLABLA'' is still in use, all queries will cease to work.
Incluso si no llamé a run()
.
Encuentro que las instrucciones deben ejecutarse exactamente en el orden en que se encuentran a continuación o si tiene problemas, ya sea con la conexión de la base de datos o la consulta. Esto funciona en 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";
}
QSqlDatabase y QSqlQuery son envoltorios livianos en implementaciones concretas, por lo que su primer ejemplo está bien. Si proporciona un nombre al agregar la conexión, o utiliza la base de datos predeterminada, simplemente escribiendo ''QSqlDatabase db (name)'' le proporciona el objeto de base de datos con muy poca sobrecarga.
removeDatabase es equivalente a cerrar el archivo (para sqlite) o la conexión (para ODBC / MySql / Postgres), por lo que suele ser algo que haría al finalizar el programa. Como dice la advertencia, debe asegurarse de que todos los objetos de bases de datos y consultas que se refieren a esa base de datos ya se hayan destruido o que sucedan cosas malas.