تبسيط العمل مع قواعد البيانات في كيو تي مع QSqlRelationalTableModel

يوم جيد ، خابروفسك! في هذه المقالة ، أود أن أتحدث عن تجربتي في تبسيط التفاعل مع قواعد بيانات SQL عند تطوير تطبيق سطح مكتب باستخدام فئة QSqlRelationalTableModel في مكتبة النظام الأساسي Qt.

مقدمة


قابلت كيو تي عندما كنت طالبًا في السنة الأولى ، بدأت للتو في برنامج C ++ ، ثم أصبحت مهتمًا جدًا بالمكتبة ، ومنذ ذلك الحين ، أتابع تحديثاتها. قبل بضعة أشهر ، في العمل ، أعطوني اختصاصًا كان من الضروري فيه تطوير تطبيق يتفاعل مع قاعدة بيانات SQLite. الهيكل الأساسي ثابت ومعروف لي من المعارف التقليدية مقدماً.

يجب أن يكون التطبيق قادرًا على تزويد المشغل بالبيانات المخزنة في قاعدة البيانات بسهولة ، والسماح بإضافة سجلات جديدة وحذف وتغيير السجلات الموجودة.

بعد ذلك ، سوف أصف بإيجاز عملية التطوير باستخدام مقتطفات من الشفرات وسأحاول شرح السبب المعقول في هذه الحالة ، تم إجراء الاختيار لصالح QSqlRelationalTableModel .

بداية التنمية


في البداية ، تقرر إنشاء تفاعل مع قاعدة البيانات باستخدام استعلامات قاعدة بيانات بسيطة ، أي اختر ، أدرج ، حذف ، والتي تسمح لك بتنفيذ جميع الوظائف الضرورية للتطبيق.

للقيام بذلك ، نحتاج إلى فصول QSqlDatabase و 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); // ,     , //   } 

بعد ذلك ، يتم تنفيذ جميع العمليات على قاعدة البيانات على النحو التالي:

 // ,    <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..."; } 

حدد تنفيذ عبارة مماثلة ، باستثناء أن البيانات لا تزال بحاجة إلى تلقي ووضعها في مكان ما:

 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()); } 

يتم تنفيذ عبارات الحذف بالطريقة نفسها تمامًا مثل إدراج لأنها لا تُرجع أي شيء.

حسنًا ، ما المشكلة؟


والحقيقة هي أنه يمكنك تنفيذ كل شيء من خلال هذه التعبيرات والاستفسارات ، لماذا نحتاج إلى نماذج؟

عندما يكون لدينا جدول واحد غير ذي صلة ، يبدو كل شيء بسيطًا جدًا ولا يتطلب إدخال أدوات إضافية. تخيل الآن أن لدينا مثل هذه الجداول ، على سبيل المثال ، 5 ، ولكل منها 5 أعمدة ، بما في ذلك المعرف. ولكل واحد اتصال مع المفتاح السابق باستخدام المفتاح الخارجي عبر المعرف ، أي عند الحذف ، من الضروري أن تتالي لحذف جميع السجلات "التابعة". يؤدي ذلك إلى عدد كبير من الطلبات ، ويبطئ عمل التطبيق بشكل كبير ، علاوة على ذلك ، من الضروري تحديث الجدول والعرض التقديمي في الواجهة في كل مرة ، مما يؤدي إلى كتابة وظائف إضافية للتحديث ، وظهور الأخطاء أو إلى خطر حدوثها ، وبشكل عام لتقليل قابلية القراءة. كود.

لهذا السبب ، خلال عملية التطوير ، اضطررت إلى التخلي عن مفهوم استخدام استعلامات SQL العارية.

تم إجراء خيارات إضافية لصالح QSqlRelationalTableModel بالتزامن مع QTableView . هناك إصدار أبسط من تنفيذ النموذج - QSqlTableModel ، الأول موروث منه ، ولديه جميع الأساليب نفسها ، لكنه يضيف القدرة على إنشاء اتصال QSqlRelation ، وهو مناسب للغاية إذا كان المستخدم بحاجة إلى إظهار معرف السجل ، ولكن اسم السجل "الأصل" الذي به متصلا.

دعونا ننظر في التنفيذ مع النماذج


فيما يلي بعض مقتطفات pod التي توضح تنفيذ النموذج / العرض.

في ملف الرأس:

 QSqlRelationalTableModel *model; 

في المنشئ:

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

يحتوي السطر أدناه على واحدة من أكثر الميزات والمزايا المريحة للنموذج على استعلامات sql - حيث يقوم بتحرير البيانات في جدول sql وإضافتها وحذفها ، حسب السياق ، عند التغيير من QTableView. الراحة هي أنك لم تعد بحاجة للتحكم في صحة حذف البيانات المتتالية وتحديثها داخل QTableView واحد.

 model->setEditStrategy(QSqlRelationalTableModel::OnFieldChange); 

يأتي بعد ذلك ميزة أخرى ملائمة توفرها هذه الفئة: يتم تأسيس اتصال بين عمودين من جداول مختلفة:

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

علاوة على ذلك ، كل شيء قياسي أكثر: select () سيقوم بتنفيذ عبارة SELECT ، وسوف يقوم setHeaderData () بتعيين النص في رؤوس 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 

الآن النموذج و tableView العمل معا وأداء وظائفهم. سيترك الرابط إلى github جميع المصادر ، حيث قمت بتنفيذها بإضافة مدخل إلى النموذج ، وحذفه ، وكذلك المرشحات.

الخاتمة


في هذه المقالة ، أردت أن أشجع جميع أولئك الذين يعملون بالفعل مع قاعدة البيانات في Qt على التخلي عن استفسارات sql العارية للمشاريع ذات التعقيد المتوسط ​​على الأقل والتحول إلى العمل مع الطرز من أجل تبسيط حياتهم وجعل الكود أكثر قابلية للقراءة والعالمية ، فقط افعل شيئًا جيدًا وجديدًا.

هذا كل شئ! آمل أن تساعد خبرتي في هذه الفصول القراء في حل مشكلة مماثلة بنجاح!

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


All Articles