C++ Qt開發:SqlRelationalTable關聯表組件

Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程序,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹SqlRelationalTable關聯表組件的常用方法及靈活運用。

在上一篇文章中詳細介紹了SqlTableModle組件是如何使用的,本篇文章將介紹SqlRelationalTable關聯表組件,該該組件其實是SqlTableModle組件的擴展類,其提供了一個帶關係的數據模型,用於處理數據庫中的表與表之間的關係。通過這個類,你可以在一個表中使用外鍵關聯到另一個表的數據上。例如將主表中的某個字段與附加表中的特定字段相關聯起來,QSqlRelation(關聯表名,關聯ID,名稱)就是用來實現多表之間快速關聯的。

1.1 ComboBox

首先我們來實現一個簡單的聯動效果,數據庫組件可以與ComboBox組件形成多級聯動效果,在日常開發中多級聯動效果應用非常廣泛,例如當我們選擇指定用戶時,讓其在另一個ComboBox組件中列舉出該用戶所維護的主機列表,又或者當用戶選擇省份時,自動列舉出該省份下面的城市列表等。

在進行聯動之前需要創建兩張表,表結構內容介紹如下:

  • User(id,name)表:存儲指定用戶的ID號與用戶名
  • UserAddressList(id,name,address)表:與User表中的用戶名相關聯,存儲該用戶所管理的主機列表信息

通過數據庫組件實現的聯動非常簡單,初始化表結構得到了兩張表,當程序運行時默認在MainWindow構造函數處填充第一個ComboBox組件,也就是執行一次數據庫查詢,並將結果通過addItem()放入到第一個組件內。

QSqlDatabase db;

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    InitMultipleSQL();

    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("./database.db");
     if (!db.open())
     {
            std::cout << db.lastError().text().toStdString()<< std::endl;
            return;
     }

     QSqlQuery query;
     query.exec("select * from User;");
     QSqlRecord rec = query.record();

     while(query.next())
     {
         int index_name = rec.indexOf("name");
         QString data_name = query.value(index_name).toString();
         ui->comboBox_user->addItem(data_name);
     }
}

而當用戶選中了第一個ComboBox組件時,則讓其轉到槽函數on_comboBox_activated(const QString &arg1)上面,如下圖所示;

該槽函數需要一個傳入參數,此參數代表組件選中的文本內容,通過利用該文本內容在數據庫內執行二次查詢並將查詢結果填充之對應的第二個ComboBox組件內即可實現組件的聯動選擇效果,其槽函數代碼如下所示;

void MainWindow::on_comboBox_user_activated(const QString &arg1)
{
    if(db.open())
    {
        QSqlQuery query;
        query.prepare("select * from UserAddressList where name = :x");
        query.bindValue(":x",arg1);
        query.exec();

        QSqlRecord rec = query.record();

        ui->comboBox_address->clear();
        while(query.next())
        {
            int index = rec.indexOf("address");
            QString data_ = query.value(index).toString();
            ui->comboBox_address->addItem(data_);
        }
    }
}

讀者可自行運行案例中的SqlComboBox案例,運行後可自行選擇不同的用戶名,則此時會輸出該用戶名所對應的地址表,如下圖所示;

1.2 TableView

接着,我們繼續以TableView組件爲例,簡單介紹一下如何實現組件與數據的綁定,首先我們需要創建一個表並插入幾條測試記錄,運行如下代碼實現建庫建表.

創建一張新表,表結構內容介紹如下:

  • LyShark(name,age)表:存儲指定用戶名與用戶年齡

在主構造函數中我們可以直接通過QSqlQueryModel來得到特定表中的記錄,並通過setHeaderData將表中的數據關聯到對應的數據模型內,最後通過setModel方法即可將對應的表數據關聯到前端顯示,其核心代碼如下所示;

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    Init();

    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("./database.db");
     if (!db.open())
     {
            std::cout << db.lastError().text().toStdString()<< std::endl;
            return;
     }

     // 查詢數據表中記錄
     qryModel=new QSqlQueryModel(this);
     qryModel->setQuery("SELECT * FROM LyShark ORDER BY id");
     if (qryModel->lastError().isValid())
     {
         return;
     }

     // 設置TableView表頭數據
     qryModel->setHeaderData(0,Qt::Horizontal,"ID");
     qryModel->setHeaderData(1,Qt::Horizontal,"Name");
     qryModel->setHeaderData(2,Qt::Horizontal,"Age");

     // 將數據綁定到模型上
     theSelection=new QItemSelectionModel(qryModel);
     ui->tableView->setModel(qryModel);
     ui->tableView->setSelectionModel(theSelection);
     ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
}

運行代碼後,程序會從數據庫內取出結果並輸出到tableView組件上,如下圖所示;

1.3 SqlRelationalTable

在最開始我們也說過,SqlRelationalTable 並不是Qt中標準的類或方法。它僅僅只是QSqlTableModel的一個子類,其支持在關係數據庫表之間建立關係,建立關聯時我們只需要使用setRelation方法即可。

setRelationQSqlRelationalTableModel 類中的一個方法,用於設置模型中某一列的關聯關係。這個方法的目的是告訴模型某一列的值在另一個表中有關聯,並提供相關的信息,以便在視圖中顯示更有意義的數據而不是外鍵的原始值。

以下是 setRelation 方法的簡單說明:

void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation);
  • column: 要設置關聯關係的列的索引。
  • relation: 包含關聯信息的 QSqlRelation 對象。

QSqlRelation 的構造函數如下:

QSqlRelation::QSqlRelation(const QString &tableName, const QString &indexColumn, const QString &displayColumn);
  • tableName: 關聯的表的名稱。
  • indexColumn: 關聯表中與當前表關聯的列的名稱,通常是外鍵列。
  • displayColumn: 關聯表中要顯示的列的名稱,通常是與外鍵列相關的實際數據。

示例:

QSqlRelationalTableModel model;
model.setTable("orders");
model.setRelation(2, QSqlRelation("customers", "customer_id", "customer_name"));
model.select();

在這個例子中,第二列(索引爲2的列)的數據將從名爲 "customers" 的表中獲取,該表的外鍵列爲 "customer_id",並且在視圖中顯示的是該關聯表的 "customer_name" 列的值。使用 setRelation 方法可以使得在表格中更容易地顯示和編輯關聯數據,而不是直接顯示外鍵的值。

在關聯表之前,我們需要設置初始化數據,此處我們提供兩個表結構,表Student用於存儲學生名字以及學生課程號,另一張Departments則用於存儲每個編號所對應的系名稱,運行代碼完成創建。

// 初始化數據表
void MainWindow::InitSQL()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("./database.db");
    if (!db.open())
           return;

    // 執行SQL創建表
    db.exec("DROP TABLE Student");
    db.exec("CREATE TABLE Student ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   "name VARCHAR(40) NOT NULL, "
                   "departID INTEGER NOT NULL)"
            );

    // 逐條插入數據
    db.exec("INSERT INTO Student(name,departID) VALUES('zhangsan',10)");
    db.exec("INSERT INTO Student(name,departID) VALUES('lisi',20)");
    db.exec("INSERT INTO Student(name,departID) VALUES('wangwu',30)");
    db.exec("INSERT INTO Student(name,departID) VALUES('wangmazi',40)");

    db.exec("DROP TABLE Departments");
    db.exec("CREATE TABLE Departments("
            "departID INTEGER NOT NULL,"
            "department VARCHAR(40) NOT NULL)"
            );

    db.exec("INSERT INTO Departments(departID,department) VALUES (10,'數學學院')");
    db.exec("INSERT INTO Departments(departID,department) VALUES (20,'物理學院')");
    db.exec("INSERT INTO Departments(departID,department) VALUES (30,'計算機學院')");
}

接着我們來看下在MainWindow構造函數中是如何進行初始化和表關聯的,以下是對代碼的簡要說明:

打開數據庫連接

創建一個 SQLite 數據庫連接,並指定了數據庫文件的路徑。如果數據庫連接成功打開,就繼續執行後面的代碼。

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./database.db");
if (!db.open())
    return;

設置主窗口的佈局和屬性

將主窗口的中央部件設置爲一個 QTableView,同時對錶格的選擇行爲和外觀進行了設置。

this->setCentralWidget(ui->tableView);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->setAlternatingRowColors(true);

打開數據表並設置模型

創建一個 QSqlRelationalTableModel 並設置了一些表格的屬性,包括表名、編輯策略、排序等。

tabModel = new QSqlRelationalTableModel(this, db);
tabModel->setTable("Student");
tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
tabModel->setSort(0, Qt::AscendingOrder);

tabModel->setHeaderData(0, Qt::Horizontal, "學號");
tabModel->setHeaderData(1, Qt::Horizontal, "姓名");
tabModel->setHeaderData(2, Qt::Horizontal, "學院");

設置查詢關係數據表

設置關係型字段,將 "學院" 列與 "Departments" 表中的 "departID" 列關聯起來,並在表格中顯示 "department" 列的數據。

tabModel->setRelation(2, QSqlRelation("Departments", "departID", "department"));

設置表格的選擇模型和代理

代碼設置了表格的選擇模型,併爲表格設置了一個關係型代理(QSqlRelationalDelegate),以便在表格中顯示關聯表的數據而不是外鍵的值。

theSelection = new QItemSelectionModel(tabModel);
ui->tableView->setModel(tabModel);
ui->tableView->setSelectionModel(theSelection);
ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));

選擇並顯示數據表

最後,通過調用 select 方法來選擇和顯示數據表的內容。

tabModel->select();

其實代碼中最重要的部分就是setRelation,我們只要確保數據庫文件正確,並且 Student 表和 Departments 表存在,並且在 Student 表中的 "學院" 列與 Departments 表中的 "departID" 列正確關聯即可,其他的就交給組件來處理,如下圖所示;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章