Simplifiez l'utilisation des bases de données dans Qt avec QSqlRelationalTableModel

Bonjour, Khabrovsk! Dans cet article, je veux parler de mon expérience de la simplification de l'interaction avec les bases de données SQL lors du développement d'une application de bureau à l'aide de la classe QSqlRelationalTableModel de la bibliothèque multiplateforme Qt.

Prologue


J'ai rencontré Qt quand j'étais étudiant en première année, commençant tout juste à programmer en C ++, en même temps je me suis sérieusement intéressé à la bibliothèque et, depuis, je suis ses mises à jour. Il y a quelques mois, au travail, ils m'ont donné un TOR dans lequel il fallait développer une application qui interagit avec la base de données SQLite. La structure de la base est fixe et connue d'avance de TK.

L'application devrait être en mesure de fournir à l'opérateur les données stockées dans la base de données, de permettre l'ajout de nouveaux enregistrements, la suppression et la modification des enregistrements existants.

Ensuite, je vais décrire brièvement le processus de développement avec des extraits de code et essayer d'expliquer raisonnablement pourquoi, dans ce cas, le choix a été fait en faveur de QSqlRelationalTableModel .

Début du développement


Initialement, il a été décidé d'établir une interaction avec la base de données à l'aide de simples requêtes de base de données, c'est-à-dire SELECT , INSERT , DELETE , qui vous permettent d'implémenter toutes les fonctions nécessaires de l'application.

Pour ce faire, nous avons besoin des classes QSqlDatabase et QSQlQuery :

QSqlDatabase db; //    db = QSqlDatabase::addDatabase("QSQLITE"); //    db.setHostName("localhost"); //  ,    db.setDatabaseName(path); //QString path -    //   if(db.open()){ qDebug() << "db opened OK..."; }else{ qDebug() << " db opening failed..."; } }else{ qDebug() << "file doesnot exist"; exit(0); // ,     , //   } 

Après cela, toutes les opérations sur la base de données sont effectuées comme suit:

 // ,    <b>QLineEdit</b>' QString query = "INSERT INTO Table (column) VALUES ('" + ui->Input->text() + "')"; QSqlQuery sqlQuery(db); //c   qDebug() << "QUERY: " << query; // //   if(sqlQuery.exec(query)){ qDebug() << "query failed..."; } else{ qDebug() << "query failed..."; } 

Les instructions Select sont exécutées de la même manière, sauf que les données doivent encore être reçues et placées quelque part:

 QString query = "SELECT id, ka FROM Table"; QSqlQuery sqlQ(db); if(!sqlQ.exec(query)) { qDebug() << "query failed..."; return; } //     // ,        while (sqlQ.next()){ //           ui->ComboBox->addItem(sqlQ.value(1).toString(),sqlQ.value(0).toInt()); } 

Les instructions de suppression sont exécutées exactement de la même manière que Insert car elles ne renvoient rien.

D'accord, quel est le problème?


Et la vérité est que vous pouvez tout implémenter à travers ces expressions et requêtes, pourquoi avons-nous besoin de modèles?

Lorsque nous avons une table indépendante, tout semble très simple et ne nécessite pas l'introduction d'outils supplémentaires. Imaginez maintenant que nous ayons de telles tables, par exemple 5, chacune avec 5 colonnes, sans inclure id. Et chacun a une connexion avec le précédent en utilisant une clé étrangère via id , c'est-à-dire lors de la suppression, il est nécessaire de cascader pour supprimer tous les enregistrements "enfants". Cela conduit à un grand nombre de requêtes, le travail de l'application ralentit considérablement, de plus, il est nécessaire de mettre à jour la table et sa présentation dans l'interface à chaque fois, ce qui conduit à écrire des fonctions supplémentaires pour la mise à jour, l'apparition de bugs ou au risque de leur occurrence, et en général pour réduire la lisibilité code.

Pour cette raison, au cours du processus de développement, j'ai dû abandonner le concept d'utilisation de requêtes SQL nues.

D'autres choix ont été faits en faveur de QSqlRelationalTableModel en conjonction avec QTableView . Il existe une version encore plus simple de l'implémentation du modèle - QSqlTableModel , la première en est héritée, elle a toutes les mêmes méthodes, mais ajoute la possibilité de créer une connexion QSqlRelation , ce qui est très pratique si l'utilisateur doit afficher non pas l'ID d'enregistrement, mais le nom de l'enregistrement «parent» avec lequel il connecté.

Regardons l'implémentation avec des modèles


Voici quelques extraits de pods montrant l'implémentation du modèle / vue.

Dans le fichier d'en-tête:

 QSqlRelationalTableModel *model; 

Dans le constructeur:

 //  ,    //  QTableView  ,    //QModelIndex     ,      connect(ui->tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &))); model = new QSqlRelationalTableModel(parent, db); // , QSqlDatabase    model->setTable("Table"); //    

La ligne ci-dessous contient l'une des fonctionnalités et des avantages les plus pratiques du modèle par rapport aux requêtes SQL - il modifie, ajoute, supprime, selon le contexte, les données de la table SQL lors du passage de à QTableView. La commodité est que vous n'avez plus besoin de contrôler l'exactitude de la suppression des données en cascade et de les mettre à jour dans un seul QTableView.

 model->setEditStrategy(QSqlRelationalTableModel::OnFieldChange); 

Vient ensuite une autre fonctionnalité pratique fournie par cette classe: une connexion est établie entre deux colonnes de tables différentes:

 //ParentTable - ,      //id - ,         //name - ,        model->setRelation(1,QSqlRelation("ParentTable", "id", "name")); 

En outre, tout est plus standard: select () exécutera une instruction SELECT et setHeaderData () définira le texte dans les en-têtes QTableView:

 model->select(); model->setHeaderData(0, Qt::Horizontal, tr("id")); model->setHeaderData(1, Qt::Horizontal, tr("id_sub")); model->setHeaderData(2, Qt::Horizontal, tr("count")); model->setHeaderData(3, Qt::Horizontal, tr("number")); model->setHeaderData(4, Qt::Horizontal, tr("data_word")); model->setHeaderData(5, Qt::Horizontal, tr("time")); model->setHeaderData(6, Qt::Horizontal, tr("name")); model->setHeaderData(7, Qt::Horizontal, tr("description")); ui->tableView->setModel(model); //     QTableView 

Maintenant, le modèle et tableView fonctionnent ensemble et remplissent leurs fonctions. Un lien vers github laissera toutes les sources, j'y ai implémenté l'ajout d'une entrée au modèle, sa suppression, ainsi que des filtres.

Conclusion


Dans cet article, je voulais encourager tous ceux qui travaillent déjà avec la base de données dans Qt à abandonner les requêtes SQL nues pour des projets de complexité au moins moyenne et à passer au travail avec des modèles afin de simplifier leur vie, rendre le code plus lisible et universel, eh bien, faites quelque chose de bien et de nouveau.

C’est tout! J'espère que mon expérience avec ces cours aidera les lecteurs à résoudre avec succès un problème similaire!

Source: https://habr.com/ru/post/fr435134/


All Articles