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. 准备工作
-  创建项目,并在 .pro 文件中加入 sql 模块: QT += core gui sql
-  将 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/
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() 方法转为字符串 
 
-  
#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)创建库 (创建库)
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)删除库
query.exec("drop database abc"); 3)创建表
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)删除表
query.exec("drop table student");5)添加
// 添加单条数据
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)查询
// 执行查询
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)更新
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)删除
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)关闭数据库
db.close();5. 可视化操作
-  Qt 提供了3个类来访问数据库,并且能够使用图形化方式来显示和操作数据 
-  QSqlTableModel : 创建一个可编辑的表格式数据模型(注意:只能应用于单表) 
-  QTableView :常见一个表格视图,可以将 QSqlTableModel 创建的模型自动填充到表格中 
-  QSqlRelationalTableModel :创建关联数据类型的数据模型 
5.1 准备工作
1.使用可视化操作时,也需要先进行数据库链接
// 链接数据库服务器
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 表中数据,并显示在表格中
实现步骤:
-  使用模型关联数据表 
-  查询数据 
-  关联模型和视图 
// 实例化 Student 表模型
stuModel = new QSqlTableModel(this);
// 1. 查询所有数据
// 设置stuModel模型关联的数据表
stuModel->setTable("student");
// 查询表中所有数据
stuModel->select();
// 将数据显示在视图中
ui->stuTableView->setModel(stuModel);设置表头
// 设置表头
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, "所属学院");设置表格修改方式为手动提交
// 设置表格隔行变色
ui->tableView->setAlternatingRowColors(true);
// 设置表格宽度
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 设置单元格修改方式
stuModel->setEditStrategy(QSqlTableModel::OnManualSubmit);5.3 添加
实现添加需要两步:
-  创建新行 QSqlRecord 
-  点击 "提交" 按钮时将新数据提交到数据库 
void Widget::on_addBtn_clicked()
{
    // 创建新数据记录
    QSqlRecord record = stuModel->record();
    // 获取数据行数
    int rowNum = stuModel->rowCount();
    // 添加一条新的空数据
    stuModel->insertRecord(rowNum, record);
}void Widget::on_pushButton_4_clicked()
{
    stuModel->submitAll();
}submitAll 方法:提交所有,包含本次添加,也可以包含同时修改的别的数据
5.4 删除
-  实现思路:点击删除时,获取所有选中的行的行号,根据行号来执行删除 
-  QItemSelectionModel: 能够获取所有选中的行(对象) 
-  QModelIndexList : 能够从选中的行对象中 获取 对应的索引号 
-  有了索引号之后,就能循环从 stuModel 中进行删除 
// 获取所有的选中行
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 撤销
void Widget::on_cancelModifyBtn_clicked()
{
    stuModel->revertAll();
    stuModel->submitAll();
}5.6 排列
方案一: 使用 tableView 进行排列
方案二: 使用 stuModel 进行排列
// 降序排列
void Widget::on_descBtn_clicked()
{
//    ui->stuTableView->sortByColumn(0, Qt::DescendingOrder);
    stuModel->sort(0, Qt::DescendingOrder);
}// 升序排列
void Widget::on_ascBtn_clicked()
{
//    ui->stuTableView->sortByColumn(0, Qt::AscendingOrder);
    stuModel->sort(0, Qt::AscendingOrder);
}5.7 搜索
核心方法:setFilter(搜索条件)
实现思路:
-  点击搜索按钮时,获取关键词 
-  拼接模糊查找的条件,调用 setFilter 进行设置 
-  重新调用 select 方法执行查询,就能将满足条件的数据显示在表格中 
// 搜索
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: 适合多表关联操作
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));


















