Qt-链接数据库&可视化操作

1. 概述

  • Qt 能够支持对常见数据库的操作,例如: MySQL、Oracle、SqlServer 等等。

  • Qt SQL模块中的API分为三层:驱动层、SQL接口层、用户接口层。

    • 驱动层为数据库和SQL接口层之间提供了底层的桥梁。

    • SQL接口层提供了对数据库的访问,包括 创建 / 删除库、表,执行增删改查的SQL语句。

    • 用户接口层提供了一种更加简便的方式将数据库中的数据链接到窗口部件上。

分层
驱动层 QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin和QSqlResult
SQL接口层 QSqlDatabase、QSqlQuery、QSqlError、QSqlField、QSqlIndex和QSqlRecord
用户接口层 QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel

2. 准备工作

  1. 创建项目,并在 .pro 文件中加入 sql 模块: QT += core gui sql

  2. 将 MySQL目录中的 libmysql.dll 和 libmysql.lib 文件复制到 Qt\5.12.3\mingw73_32\bin 目录下

如果不行,则去官网自己找 : MySQL :: Download MySQL Connector/C (Archived Versions)https://downloads.mysql.com/archives/c-c/

3. 链接数据库

  • Qt 默认支持一些驱动,可以通过 QSqlDatabase::drivers() 方法查看Qt支持的驱动类型。

  • 链接到数据库服务器需要使用 QSqlDatabase 类

  • 主要方法:

    • QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); // 链接MySQL服务器

    • db.setHostName("127.0.0.1"); // 设置服务器地址

    • db.setPort(3306); // 设置端口号,如果使用默认端口号可不调用该方法

    • db.setUserName("root"); // 设置用户名

    • db.setPassword("root"); // 设置密码

    • db.setDatabaseName("study"); // 设置要操作的数据库

    • db.open() // 打开数据库链接,返回值 bool

  • 其他方法

    • db.lastError(); // 获取上一次的错误信息, 返回一个 QSqlError 对象,调用 text() 方法转为字符串
cpp 复制代码
#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>

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

    // 打印支持的数据库驱动
    qDebug() << QSqlDatabase::drivers();

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("127.0.0.1");
    db.setPort(3306);
    db.setUserName("root");
    db.setPassword("root");
		db.setDatabaseName("study");

    // 当数据库打开失败时,提示信息
    if(db.open())
    {
        qDebug() << "链接成功";
    }
    else
    {
        qDebug() << "链接失败" << db.lastError().text();
    }
}

4. 执行SQL语句

  • 执行 SQL 语句需要依靠 QSqlQuery 类

    • exec(QString sql) 方法用来执行sql语句

    • prepare() 方法执行预处理

    • execBatch() 执行预处理的SQL语句

1)创建库 (创建库)

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
  ui->setupUi(this);

  // 打印支持的数据库驱动
  qDebug() << QSqlDatabase::drivers();

  QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
  db.setHostName("127.0.0.1");
  db.setPort(3306);
  db.setUserName("root");
  db.setPassword("root");

  // 当数据库打开失败时,提示信息
  if (!db.open())
  {
    QMessageBox::critical(this, "警告", db.lastError().text());
    return ;
  }

  // 创建 QSqlQuery 对象
  QSqlQuery query;

  query.exec("create database abc");      // 创建库

}

2)删除库

cpp 复制代码
query.exec("drop database abc"); 

3)创建表

cpp 复制代码
query.exec(
  "create table student( \
     sno int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, \
     sname varchar(30) not null unique, \
     sage  tinyint UNSIGNED, \
     sgender enum('男', '女') DEFAULT '男' \
   )ENGINE=MyISAM CHARSET=utf8"
);

4)删除表

cpp 复制代码
query.exec("drop table student");

5)添加

cpp 复制代码
// 添加单条数据
query.exec("insert into student values(1, '关羽', 30, '男')");
query.exec("insert into student values(null, '张飞', 28, '男')");

// 批量添加 --- 方式1 
// ? 叫做占位符
query.prepare("insert into student(sname, sage, sgender) values(?,?,?)");

QVariantList nameList;
nameList << "aaa" << "bbb" << "ccc";
QVariantList ageList;
ageList << 20 << 30 << 40;
QVariantList genderList;
genderList << "男" << "女" << "男";

query.addBindValue(nameList);
query.addBindValue(ageList);
query.addBindValue(genderList);

query.execBatch();


// 批量添加 - 方式2
// :xxx : 数据名称
query.prepare("insert into student(sname, sage, sgender) values(:name, :age, :gender)");

QVariantList nameList;
nameList << "小王" << "小张" << "小赵";
QVariantList ageList;
ageList << 20 << 30 << 40;
QVariantList genderList;
genderList << "男" << "女" << "男";

query.bindValue(":name", nameList);
query.bindValue(":age", ageList);
query.bindValue(":gender", genderList);

query.execBatch();

6)查询

cpp 复制代码
// 执行查询
query.exec("select * from student");

// 判断查询结果中是否有下一条数据
while (query.next()) {
  // value 方法用来设置列数据项,结果可以继续转为具体的数据类型
  qDebug() 
    << query.value(0).toInt()
    << query.value(1).toString()
    << query.value("sage").toInt()
    << query.value("sgender").toString();
}

7)更新

cpp 复制代码
query.exec("update student set sage=35 where sno=1");

// 注意事项: 字符串字段需要加引号
QString sql = QString("update student set sname='%1',sage=%2,sgender='%3' where sno=%4").arg("孙尚香").arg(20).arg("女").arg(1);
query.exec(sql);

8)删除

cpp 复制代码
query.exec("delete from student where sno=2");

// 批量删除
QString sql = QString("delete from student where sno in (%1)").arg("1,2,3");
query.exec(sql);

9)关闭数据库

cpp 复制代码
db.close();

5. 可视化操作

  • Qt 提供了3个类来访问数据库,并且能够使用图形化方式来显示和操作数据

  • QSqlTableModel : 创建一个可编辑的表格式数据模型(注意:只能应用于单表)

  • QTableView :常见一个表格视图,可以将 QSqlTableModel 创建的模型自动填充到表格中

  • QSqlRelationalTableModel :创建关联数据类型的数据模型

5.1 准备工作

1.使用可视化操作时,也需要先进行数据库链接

cpp 复制代码
// 链接数据库服务器
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setUserName("root");
db.setPassword("root");
db.setDatabaseName("study");

if (!db.open())
{
  qDebug() << db.lastError().text();
}

2.设置ui界面

5.2 显示表格(R)

目标:查询 student 表中数据,并显示在表格中

实现步骤:

  1. 使用模型关联数据表

  2. 查询数据

  3. 关联模型和视图

cpp 复制代码
// 实例化 Student 表模型
stuModel = new QSqlTableModel(this);


// 1. 查询所有数据
// 设置stuModel模型关联的数据表
stuModel->setTable("student");
// 查询表中所有数据
stuModel->select();
// 将数据显示在视图中
ui->stuTableView->setModel(stuModel);

设置表头

cpp 复制代码
// 设置表头
stuModel->setHeaderData(0, Qt::Horizontal, "学号");
stuModel->setHeaderData(1, Qt::Horizontal, "姓名");
stuModel->setHeaderData(2, Qt::Horizontal, "昵称");
stuModel->setHeaderData(3, Qt::Horizontal, "性别");
stuModel->setHeaderData(4, Qt::Horizontal, "年龄");
stuModel->setHeaderData(5, Qt::Horizontal, "入学时间");
stuModel->setHeaderData(6, Qt::Horizontal, "所属学院");

设置表格修改方式为手动提交

cpp 复制代码
// 设置表格隔行变色
ui->tableView->setAlternatingRowColors(true);

// 设置表格宽度
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
cpp 复制代码
// 设置单元格修改方式
stuModel->setEditStrategy(QSqlTableModel::OnManualSubmit);

5.3 添加

实现添加需要两步:

  1. 创建新行 QSqlRecord

  2. 点击 "提交" 按钮时将新数据提交到数据库

cpp 复制代码
void Widget::on_addBtn_clicked()
{
    // 创建新数据记录
    QSqlRecord record = stuModel->record();
    // 获取数据行数
    int rowNum = stuModel->rowCount();
    // 添加一条新的空数据
    stuModel->insertRecord(rowNum, record);
}
cpp 复制代码
void Widget::on_pushButton_4_clicked()
{
    stuModel->submitAll();
}

submitAll 方法:提交所有,包含本次添加,也可以包含同时修改的别的数据

5.4 删除

  • 实现思路:点击删除时,获取所有选中的行的行号,根据行号来执行删除

  • QItemSelectionModel: 能够获取所有选中的行(对象)

  • QModelIndexList : 能够从选中的行对象中 获取 对应的索引号

  • 有了索引号之后,就能循环从 stuModel 中进行删除

cpp 复制代码
// 获取所有的选中行
QItemSelectionModel *selectedModel = ui->tableView->selectionModel();


// 获取选中行的行号
QModelIndexList indexList = selectedModel->selectedRows();

for (int i = 0; i < indexList.size(); i++)
{
  qDebug() << i << indexList[i].row();
  stuModel->removeRow(indexList[i].row());
}

5.5 撤销

cpp 复制代码
void Widget::on_cancelModifyBtn_clicked()
{
    stuModel->revertAll();
    stuModel->submitAll();
}

5.6 排列

方案一: 使用 tableView 进行排列

方案二: 使用 stuModel 进行排列

cpp 复制代码
// 降序排列
void Widget::on_descBtn_clicked()
{
//    ui->stuTableView->sortByColumn(0, Qt::DescendingOrder);
    stuModel->sort(0, Qt::DescendingOrder);
}
cpp 复制代码
// 升序排列
void Widget::on_ascBtn_clicked()
{
//    ui->stuTableView->sortByColumn(0, Qt::AscendingOrder);
    stuModel->sort(0, Qt::AscendingOrder);
}

5.7 搜索

核心方法:setFilter(搜索条件)

实现思路:

  1. 点击搜索按钮时,获取关键词

  2. 拼接模糊查找的条件,调用 setFilter 进行设置

  3. 重新调用 select 方法执行查询,就能将满足条件的数据显示在表格中

cpp 复制代码
// 搜索
void Widget::on_searchBtn_clicked()
{
    // 获取搜索关键词
    QString str = ui->lineEdit->text();
    // 构建模糊查询
    QString filterStr = QString("sname like '%%1%'").arg(str);
    // 过滤
    stuModel->setFilter(filterStr);
    // 查询
    stuModel->select();
}

5.8 关联表设置

QSqlTableModel: 适合单表操作

QSqlRelationalTableModel: 适合多表关联操作

cpp 复制代码
stuModel = new QSqlRelationalTableModel(this);
stuModel->setTable("student");

// 设置 student 与 dept 的关联关系
stuModel->setRelation(6, QSqlRelation("dept", "dno", "dname"));
stuModel->select();

// 设置学科列的可选项只能是 dept 表中的数据
ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));
相关推荐
猷咪9 分钟前
C++基础
开发语言·c++
IT·小灰灰10 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧12 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q13 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳013 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾13 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB16 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683617 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
打工的小王22 分钟前
redis(四)搭建哨兵模式:一主二从三哨兵
数据库·redis·缓存
星火开发设计30 分钟前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识