Quelle est la bonne méthode de QSqlDatabase & QSqlQuery?

j'ai confondu avec le manuel , je devrais travailler comme ceci:

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

QSqlDatabase::removeDatabase (...);

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

Eh bien, si je cache db à l'intérieur d'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 mises en garde de la forme:

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

Même si je n'ai pas l'appeler run().

27
demandé sur alexisdm 2011-10-06 07:13:59

3 réponses

quand vous créez un QSqlDatabase objet addDatabase ou lorsque vous appelez removeDatabase, vous êtes simplement à associer ou de dissociant d'un n-uplet (pilote, nom d'hôte:port, nom de 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 de 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 pour l'ensemble de l'application. Donc, si vous appelez addDatabase dans chacun des objets qui l'utilisent, vous changez tout QSqlDatabase objets qui utilisent le même nom de connexion et invalident 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 connexion, en s'assurant que:

  • QSqlQuery sont détachés de l' QSqlDatabase avant de fermer la base de données en appelant QSqlQuery::finish(), qui est automatique lorsque le QSqlQuery objet sort de portée,
  • QSqlDatabase avec le même nom de connexion sont close() d Quand vous appelez QSqlDatabase::removeDatabase (close() est aussi appelé automatiquement lorsque le QSqlDatabase l'objet sort du champ d'application).

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

  1. un seul QSqlDatabase instance dans une seule classe (par exemple, dans votre mainwindow), et l'utiliser dans d'autres objets qui en ont besoin soit en passant le QSqlDatabase directement ou tout simplement le nom de la connexion que vous passez à QSqlDatabase::database pour obtenir l' QSqlDatabase instance. QSqlDatabase::databaseQHash pour récupérer un QSqlDatabase d'après son nom, il est donc probablement plus lent que de passer le QSqlDatabase objet directement entre les objets et les fonctions, et si vous utilisez la connexion par défaut, vous n'avez même pas à passer quoi que ce soit n'importe où, il suffit d'appeler QSqlDatabase::database() sans 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. configurer le QSqlDatabase une fois, ouvrez - le pour vérifier que les paramètres sont corrects, et laissez tomber 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 devriez pas fermer la base de données explicitement, parce que vous pouvez avoir plusieurs objets utilisant la même connexion de base de données d'une manière rentrante (par exemple, si une fonction utilise connexion et appels B qui utilisent également la connexion. Si B ferme la connexion avant de retourner le contrôle à A, la connexion sera également fermée pour A, ce qui est probablement une mauvaise chose).

42
répondu alexisdm 2011-10-07 15:38:15

QSqlDatabase et QSqlQuery sont des enveloppements légers autour d'implémentations en béton, donc votre premier exemple est parfait. Si vous fournissez un nom lors de l'ajout de la connexion, ou utilisez la base de données par défaut, alors simplement en écrivant 'qsqldatabase db(name)' Vous donne l'objet de base de données avec très peu de frais généraux.

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

3
répondu James Turner 2011-10-06 18:05:35

je trouve que les instructions doivent être exécutées exactement dans l'ordre où elles se trouvent ci-dessous ou bien vous avez des problèmes, soit avec la connexion à la base de données, soit avec la requête. Cela fonctionne 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";
}
0
répondu Pescolly 2013-09-14 06:19:09