C++ Qt開發:QSqlDatabase數據庫組件

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

Qt SQL模塊是Qt框架的一部分,它提供了一組類和函數,用於在Qt應用程序中進行數據庫操作。這個模塊的目標是簡化數據庫訪問和操作,並提供一致的接口,使得開發者可以方便地與不同數據庫系統進行交互。一般SQL組件常用的操作,包括讀取數據、插入數據、更新數據、刪除數據功能,這四個功能我將分別介紹它是如何使用的。

在使用此模塊時必須要引入對應文件,需要在*.pro文件內增加QT += sql,並在頭文件內導入QSqlDatabase模塊纔可以正常使用,該模塊是用於管理數據庫連接的核心類之一。它提供了一系列方法,使得在Qt應用程序中進行數據庫操作變得方便和靈活。QSqlDatabase類的靈活性使得開發者能夠與多種數據庫系統(如SQLite、MySQL、PostgreSQL等)進行交互,而不必擔心底層數據庫細節。這有助於實現跨數據庫的可移植性和更高層次的數據庫訪問抽象。

下面是QSqlDatabase類中一些常用的方法,以表格形式進行說明和概述:

方法 描述
QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatin1String(defaultConnection)) 添加一個數據庫連接,其中type指定數據庫驅動類型,connectionName指定連接的名稱,默認爲默認連接。返回創建的數據庫連接對象。
QSqlDatabase::database(const QString &connectionName = QLatin1String(defaultConnection), bool open = true) 獲取指定連接名稱的數據庫連接對象。如果數據庫連接不存在,會創建一個新的連接。如果opentrue,則嘗試打開數據庫連接。
QSqlDatabase::removeDatabase(const QString &connectionName = QLatin1String(defaultConnection)) 移除指定連接名稱的數據庫連接。如果該連接當前處於打開狀態,則會被關閉。
QSqlDatabase::setHostName(const QString &host) 設置數據庫服務器的主機名。
QSqlDatabase::setDatabaseName(const QString &name) 設置要連接的數據庫的名稱。
QSqlDatabase::setUserName(const QString &name) 設置用於連接數據庫的用戶名。
QSqlDatabase::setPassword(const QString &password) 設置用於連接數據庫的密碼。
QSqlDatabase::setPort(int port) 設置數據庫服務器的端口號。
QSqlDatabase::open() 打開數據庫連接。如果連接成功,返回true,否則返回false
QSqlDatabase::close() 關閉數據庫連接。
QSqlDatabase::isOpen() 判斷數據庫連接是否打開。返回true表示連接已打開,false表示連接未打開。
QSqlDatabase::tables(QSql::TableType type = QSql::Tables) 返回數據庫中的表的列表。可以指定表的類型,如QSql::Tables表示用戶表,QSql::SystemTables表示系統表。
QSqlDatabase::commit() 提交當前事務。
QSqlDatabase::rollback() 回滾當前事務。
QSqlDatabase::transaction() 開始一個新事務。

這些方法提供了管理和操作數據庫連接的基本功能,包括連接數據庫、設置連接參數、打開和關閉連接、執行事務等。在實際使用中,開發者可以根據需要選擇適當的方法來管理數據庫連接和執行數據庫操作。

1.1 逐條記錄插入

初始化數據庫我們可以通過調用QSqlDatabase::addDatabase來打開,在打開參數中支持多種數據庫類型的選擇,包括但不限於 SQLiteMySQLPostgreSQLOracleODBC 等,每種數據庫類型對應一個特定的驅動,開發者可以通過指定數據庫類型和連接名稱創建相應的數據庫連接。

QSqlDatabase::addDatabase 是一個靜態方法,用於嚮應用程序中添加一個數據庫連接。此方法允許你爲不同的數據庫類型添加連接,並且你可以爲每個連接指定一個唯一的名稱。

static QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatin1String(defaultConnection));

參數說明

  • type: 字符串,表示數據庫的類型,例如 "QSQLITE"、"QMYSQL" 等,具體取決於你所使用的數據庫驅動。
  • connectionName: 字符串,表示連接的名稱,默認爲 defaultConnection。可以通過這個名稱在應用程序中區分不同的數據庫連接。

返回值

返回創建的 QSqlDatabase 對象,可以使用這個對象進行進一步的數據庫配置和操作。如果不提供連接名稱,將使用默認的連接名稱 defaultConnection。在一個應用程序中,你可以同時擁有多個數據庫連接,每個連接都有一個唯一的名稱。

在打開後接着我們就可以通過執行db.exec()的方式向特定數據庫內插入數據,如下代碼所示,通過在編輯框內提取出所需參數並對數據庫進行初始化,當出事後成功後則調用db.exec()函數插入記錄,最後通過db.commit()提交事務刷新到數據庫中。

void MainWindow::on_pushButton_clicked()
{
    // 指定數據庫驅動類型
    QSqlDatabase db = QSqlDatabase::addDatabase(ui->lineEdit_type->text());
    db.setDatabaseName(ui->lineEdit_dir->text());
     if (!db.open())
     {
            QMessageBox::information(nullptr, "信息", db.lastError().text(), QMessageBox::Ok);
     }

     // 獲取文本內容
     QString plainText = ui->plainTextEdit->toPlainText();

     // 使用 split() 函數分割成行
     QStringList lines = plainText.split('\n', Qt::SkipEmptyParts);

     // 遍歷每一行
     for (const QString &line : lines)
     {
         db.exec(line);
     }

    // 提交事務請求
    bool ref = db.commit();
    if(ref == true)
    {
        QMessageBox::information(nullptr, "信息", "初始化失敗", QMessageBox::Ok);
    }
    else
    {
        QMessageBox::information(nullptr, "信息", "執行初始化成功", QMessageBox::Ok);
    }

    db.close();
}

在初始化部分,我們通過create table語句創建一個LyShark的數據表,並插入三個字段,分別是id,name,age,當點擊初始化時則會使用QSQLITE引擎,在當前目錄下生成一個名爲database.sqlite的數據庫文件;

同理,只要準備合理的SQL語句就可以實現對應的數據庫記錄的插入功能;

1.2 多條記錄插入

多條記錄的插入依賴於QSqlQuery類,該類是Qt中用於執行和處理SQL查詢的類。它允許你向數據庫發送 SQL 查詢並檢索查詢結果。以下是QSqlQuery類中一些常用的方法,以表格形式進行說明和概述:

方法 描述
QSqlQuery::QSqlQuery(QSqlDatabase db = QSqlDatabase()) 構造函數,創建一個數據庫查詢對象。如果提供了數據庫連接對象 db,則該查詢對象將與指定的數據庫連接關聯。
QSqlQuery::~QSqlQuery() 析構函數,釋放查詢對象。在對象銷燬時,會自動關閉查詢。
exec(const QString &query) 執行指定的 SQL 查詢。返回 true 表示執行成功,false 表示執行失敗。
execBatch(BatchExecutionMode mode = ValuesAsRows) 批量執行多個 SQL 查詢。可以設置批量執行模式。
isActive() const 判斷查詢是否處於活動狀態(已執行並且未關閉)。
isSelect() const 判斷當前查詢是否是 SELECT 查詢。
lastError() const 獲取最後一次執行的查詢的錯誤信息。如果查詢成功,返回一個空的 QSqlError 對象。
next() 移動到結果集中的下一條記錄。返回 true 表示移動成功,false 表示已經沒有更多記錄。
seek(int index, bool relative = false) 定位到結果集中的指定記錄。
value(int index) 獲取當前記錄中指定列的值。
value(const QString &name) 獲取當前記錄中指定列名的值。
prepare(const QString &query) 準備一個 SQL 查詢。可以在查詢中使用佔位符 ? 作爲參數的佔位符。
bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType type = QSql::In) 綁定參數到查詢。可以使用佔位符 ? 或者命名佔位符 :name
executedQuery() const 獲取實際執行的 SQL 查詢。當使用佔位符時,這個方法返回實際執行的 SQL 語句。
record() const 獲取查詢的元數據信息,包括字段名、字段類型等。
size() const 獲取結果集的記錄數。

這些方法提供了一些基本的數據庫查詢和結果處理功能,包括執行查詢、處理結果、錯誤處理、綁定參數、獲取元數據等。在實際應用中,可以根據具體的需求選擇適當的方法來操作數據庫。

如下代碼所示,首先我們通過split的方式將兩個plainEditText中的內容進行分割,並分別將結果存儲至QStringList容器內,接着通過使用query.prepare綁定一個SQL語句對應關係,並通過循環的方式以此插入數據,代碼如下所示;

void MainWindow::on_pushButton_3_clicked()
{
    // 指定數據庫驅動類型
    QSqlDatabase db = QSqlDatabase::addDatabase(ui->lineEdit_type->text());
    db.setDatabaseName(ui->lineEdit_dir->text());
    if (!db.open())
    {
        QMessageBox::information(nullptr, "信息", db.lastError().text(), QMessageBox::Ok);
    }

    // 定義字符串鏈表
    QStringList user_name;
    QStringList user_age;

    // 獲取文本內容
    QString plainText_uname = ui->plainTextEdit_uname->toPlainText();
    QString plainTextEdit_uage = ui->plainTextEdit_uage->toPlainText();

    // 使用 split() 函數分割成行
    QStringList lines_uname = plainText_uname.split('\n', Qt::SkipEmptyParts);
    QStringList lines_uage = plainText_uname.split('\n', Qt::SkipEmptyParts);

    // 遍歷每一行
    for (const QString &line : lines_uname)
    {
        user_name.append(line);
    }

    for (const QString &line : lines_uage)
    {
        user_age.append(line);
    }

    // 綁定數據記錄
    QSqlQuery query;
    query.prepare("INSERT INTO LyShark(name,age) ""VALUES (:name, :age)");

    // 判斷兩張表中字段數據量是否一致
    if(user_name.size() == user_age.size())
    {
        // 循環插入數據
        for(int x=0;x< user_name.size(); x++)
        {
            query.bindValue(":name",user_name[x]);
            query.bindValue(":age",user_age[x]);
            query.exec();
        }
    }

    // 提交事務請求
    bool ref = db.commit();
    if(ref == true)
    {
        QMessageBox::information(nullptr, "信息", "插入數據失敗", QMessageBox::Ok);
    }
    else
    {
        QMessageBox::information(nullptr, "信息", "插入數據成功", QMessageBox::Ok);
    }

    db.close();
}

運行後則可以將如下所示的字段依次插入到數據庫中存儲,如下圖所示;

1.3 查詢表中記錄

查詢表中記錄離不開QSqlRecord 類,它是Qt中用於表示數據庫記錄(行)的元數據的類。提供了關於記錄中字段(列)的信息,包括字段名、字段類型等。通常用於表示數據庫查詢的結果集中的一行記錄的元數據,以便在程序中處理這些記錄的信息。

以下是QSqlRecord類中一些常用的方法,以表格形式進行說明和概述:

方法 描述
QSqlRecord::QSqlRecord(const QSqlRecord &other) 複製構造函數,創建一個 QSqlRecord 對象,複製另一個記錄的信息。
QSqlRecord::~QSqlRecord() 析構函數,釋放 QSqlRecord 對象。
append(const QSqlField &field) 向記錄中添加一個字段。
clear() 清空記錄中的所有字段。
field(int index) const 獲取指定索引的字段信息。
field(const QString &name) const 獲取指定字段名的字段信息。
fieldName(int index) const 獲取指定索引的字段名。
indexOf(const QString &name) const 獲取指定字段名的索引。如果字段不存在,返回 -1。
isEmpty() const 判斷記錄是否爲空(沒有字段)。
isGenerated(int index) const 判斷指定索引的字段是否爲自動生成的。
setGenerated(int index, bool generated) 設置指定索引的字段是否爲自動生成的。
setGenerated(const QString &name, bool generated) 設置指定字段名的字段是否爲自動生成的。
setField(int index, const QSqlField &field) 設置指定索引的字段信息。
count() const 獲取記錄中字段的數量。
contains(const QString &name) const 判斷記錄中是否包含指定字段名的字段。
operator=() 賦值運算符重載,將一個記錄的內容複製給另一個記錄。

這些方法提供了一些基本的記錄處理功能,包括添加字段、獲取字段信息、設置字段信息、判斷字段是否存在等。在實際應用中,可以根據具體的需求選擇適當的方法來操作記錄。

在查詢數據時,我們只需要通過QSqlQuery得到完整的數據表記錄條數,然後就可以使用QSqlRecord來創建一個查詢對象,此時每次調用query.next()都會向後遍歷一行記錄,通過rec.indexOf就可以得到對應字段的參數值,而query.value則可以將其提取出來,最後我們將其插入到TreeWidget中用於展示,代碼如下所示;

// 查詢表中數據
void MainWindow::on_pushButton_4_clicked()
{
    // 指定數據庫驅動類型
    QSqlDatabase db = QSqlDatabase::addDatabase(ui->lineEdit_type->text());
    db.setDatabaseName(ui->lineEdit_dir->text());
    if (!db.open())
    {
        QMessageBox::information(nullptr, "信息", db.lastError().text(), QMessageBox::Ok);
    }

    // 查詢數據
    QSqlQuery query("SELECT * FROM LyShark;",db);
    QSqlRecord rec = query.record();

    // 循環所有記錄
    while(query.next())
    {
        // 判斷當前記錄是否有效
        if(query.isValid())
        {
            // 讀出數據
            int id_ptr = rec.indexOf("id");
            int id_value = query.value(id_ptr).toInt();

            int name_ptr = rec.indexOf("name");
            QString name_value = query.value(name_ptr).toString();

            int age_ptr = rec.indexOf("age");
            int age_value = query.value(age_ptr).toInt();

            // 設置treeWidget屬性
            ui->treeWidget->setColumnCount(3);         // 設置總列數
            ui->treeWidget->setColumnWidth(0,300);     // 設置最後一列寬度自適應
            ui->treeWidget->setIndentation(0);         // 設置表頭縮進爲0

            // 設置表頭數據
            QStringList headers;
            headers.append("UID");
            headers.append("用戶名");
            headers.append("年齡");

            ui->treeWidget->setHeaderLabels(headers);

            // 模擬插入數據到表中
            QTreeWidgetItem* item=new QTreeWidgetItem();
            item->setText(0,QString::number(id_value));
            item->setText(1,name_value);
            item->setText(2,QString::number(age_value));
            ui->treeWidget->addTopLevelItem(item);
        }
    }
}

編譯並運行程序,當點擊查詢按鈕時,則可以將數據庫中的數據輸出到組件中顯示,如下圖所示;

1.5 更新表中記錄

最後一項是對記錄的更新,其實更新記錄同樣是使用exec()函數,只不過是將插入語句修改爲了update而已,如下代碼通過數據庫查詢並根據特定條件填充了界面上的兩個文本框 (ui->lineEdit_select_unameui->lineEdit_select_uage)。下面是這段代碼的概述:

  1. 建立數據庫連接:
    • 通過 QSqlDatabase::addDatabase 設置數據庫的驅動類型,例如 "QSQLITE"、"QMYSQL" 等。
    • 使用 setDatabaseName 設置數據庫名稱,這可能是一個本地文件名或者服務器地址。
    • 嘗試打開數據庫連接,如果連接失敗,通過 QMessageBox 顯示錯誤信息。
  2. 執行數據庫查詢:
    • 使用 QSqlQuery 對象執行 SQL 查詢語句 "SELECT * FROM LyShark;"。
    • 通過 QSqlRecord 獲取查詢結果的記錄結構,包括字段名和字段類型。
  3. 循環處理查詢結果:
    • 使用 while (query.next()) 循環遍歷查詢結果中的每一行記錄。
    • 對於每個有效的記錄,獲取 "id" 字段的值,並與用戶輸入的 "uid" 進行匹配。
    • 如果匹配成功,獲取 "name" 和 "age" 字段的值,並將其分別填充到 ui->lineEdit_select_unameui->lineEdit_select_uage 中。
    • 如果沒有匹配的記錄,或者記錄無效,將 ui->lineEdit_select_unameui->lineEdit_select_uage 的文本設置爲 "-1"。

這段代碼主要完成了從數據庫查詢數據並將結果填充到用戶界面的操作。需要注意的是,如果涉及用戶輸入的 ui->lineEdit_select_uid->text() 不是數字,可能需要額外的驗證和處理。此外,數據庫的表結構和字段名需要與代碼中的對應關係一致。

void MainWindow::on_pushButton_5_clicked()
{
    // 指定數據庫驅動類型
    QSqlDatabase db = QSqlDatabase::addDatabase(ui->lineEdit_type->text());
    db.setDatabaseName(ui->lineEdit_dir->text());
    if (!db.open())
    {
        QMessageBox::information(nullptr, "信息", db.lastError().text(), QMessageBox::Ok);
    }

    // 查詢數據
    QSqlQuery query("SELECT * FROM LyShark;",db);
    QSqlRecord rec = query.record();

    // 循環所有記錄
    while(query.next())
    {
        // 判斷當前記錄是否有效
        if(query.isValid())
        {
            // 讀出數據
            int id_ptr = rec.indexOf("id");
            int id_value = query.value(id_ptr).toInt();

            // 如果是則填充表格
            if(QString::number(id_value) == ui->lineEdit_select_uid->text())
            {
                int name_ptr = rec.indexOf("name");
                QString name_value = query.value(name_ptr).toString();
                ui->lineEdit_select_uname->setText(QString(name_value.data()));

                int age_ptr = rec.indexOf("age");
                QString age_value = query.value(age_ptr).toString();
                ui->lineEdit_select_uage->setText(QString(age_value.data()));
            }
        }
        else
        {
            ui->lineEdit_select_uname->setText("-1");
            ui->lineEdit_select_uage->setText("-1");
        }
    }
}

讀者可通過輸入一個唯一的標識符,例如UID號,來實現對特定數據的查詢功能,如下圖所示;

數據的跟新只需要調用update語句即可實現,其他的功能與查詢保持一致,如下代碼實現了數據的共噁心操作,以下是代碼的概述:

  1. 建立數據庫連接:
    • 使用 QSqlDatabase::addDatabase 設置數據庫的驅動類型,例如 "QSQLITE"、"QMYSQL" 等。
    • 使用 setDatabaseName 設置數據庫名稱,這可能是一個本地文件名或者服務器地址。
    • 嘗試打開數據庫連接,如果連接失敗,通過 QMessageBox 顯示錯誤信息。
  2. 執行數據庫更新:
    • 從用戶界面的輸入框中獲取更新所需的數據,包括 uidname、和 age
    • 構建 SQL 更新語句,例如 UPDATE LyShark SET name='newName', age=25 WHERE id=123;
    • 使用 db.exec(sql) 執行 SQL 更新語句。
  3. 事務的提交和關閉:
    • 嘗試提交事務,如果成功,顯示更新數據成功的消息,否則顯示更新數據失敗的消息。
    • 關閉數據庫連接。

需要注意:

  • 在一般情況下,Qt 的數據庫操作會自動處理事務,你不必顯式調用 commit()
  • 使用 std::cout 輸出日誌不太符合 Qt 的風格,Qt 提供了 qDebug() 用於輸出調試信息。
  • 對於事務,通常在更新操作後關閉數據庫連接,而不是在提交事務之前。
void MainWindow::on_pushButton_6_clicked()
{
    // 指定數據庫驅動類型
    QSqlDatabase db = QSqlDatabase::addDatabase(ui->lineEdit_type->text());
    db.setDatabaseName(ui->lineEdit_dir->text());
    if (!db.open())
    {
        QMessageBox::information(nullptr, "信息", db.lastError().text(), QMessageBox::Ok);
    }

    // 執行SQL更新記錄
    int uid = ui->lineEdit_select_uid->text().toInt();
    QString name = ui->lineEdit_select_uname->text();
    int age = ui->lineEdit_select_uage->text().toInt();

    QString sql = QString("UPDATE LyShark SET name='%1', age=%2 WHERE id=%3;").arg(name).arg(age).arg(uid);
    db.exec(sql);
    std::cout << "update => " << sql.toStdString() << std::endl;

    // 提交事務請求
    bool ref = db.commit();
    if(ref == true)
    {
        QMessageBox::information(nullptr, "信息", "更新數據失敗", QMessageBox::Ok);
    }
    else
    {
        QMessageBox::information(nullptr, "信息", "更新數據成功", QMessageBox::Ok);
    }

    db.close();
}

讀者可通過輸入一個UID編號查詢數據記錄,接着在修改對應的字段值,並點擊更新按鈕刷新數據庫,如下圖所示將第一個記錄的姓名刷新爲lyshark

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