C++ Qt开发:QSqlDatabase数据库组件

C/C++
183
0
0
2024-03-02

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)

获取指定连接名称的数据库连接对象。如果数据库连接不存在,会创建一个新的连接。如果open为true,则尝试打开数据库连接。

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)。下面是这段代码的概述:

  • 建立数据库连接:通过 QSqlDatabase::addDatabase 设置数据库的驱动类型,例如 "QSQLITE"、"QMYSQL" 等。
  • 使用 setDatabaseName 设置数据库名称,这可能是一个本地文件名或者服务器地址。
  • 尝试打开数据库连接,如果连接失败,通过 QMessageBox 显示错误信息。
  • 执行数据库查询:使用 QSqlQuery 对象执行 SQL 查询语句 "SELECT * FROM LyShark;"。
  • 通过 QSqlRecord 获取查询结果的记录结构,包括字段名和字段类型。
  • 循环处理查询结果:使用 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语句即可实现,其他的功能与查询保持一致,如下代码实现了数据的共恶心操作,以下是代码的概述:

  • 建立数据库连接:使用 QSqlDatabase::addDatabase 设置数据库的驱动类型,例如 "QSQLITE"、"QMYSQL" 等。
  • 使用 setDatabaseName 设置数据库名称,这可能是一个本地文件名或者服务器地址。
  • 尝试打开数据库连接,如果连接失败,通过 QMessageBox 显示错误信息。
  • 执行数据库更新:从用户界面的输入框中获取更新所需的数据,包括 uidname、和 age
  • 构建 SQL 更新语句,例如 UPDATE LyShark SET name='newName', age=25 WHERE id=123;
  • 使用 db.exec(sql) 执行 SQL 更新语句。
  • 事务的提交和关闭:尝试提交事务,如果成功,显示更新数据成功的消息,否则显示更新数据失败的消息。
  • 关闭数据库连接。

需要注意:

  • 在一般情况下,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