0:前言
🪧 什么情况需要数据库?
- 1 大规模的数据需要处理(比如上千上万的数据量)
- 2 需要把数据信息存储起来,无论是本地还是服务上,而不是断电后数据信息就消失了。
如果不是上面的原因化,一般可以使用数组来处理。
🪧一般常使用的数据库驱动是MYSQL和QSQLITE。二者区别在于,前者用于服务器存储信息,后者用于本地存储信息。并且QSQLITE主要用于嵌入式,占用资源非常低,占用内存小,通常几百k就搞定。’
这里博主因为对
MySQL
熟悉一些,就使用MySQL
来进行数据库的连接
一、Mysql的安装
因为我们项目的方案是程序的运行以及相关数据的存储都在一台主机上,所以不论打不打包。首先要在主机上安装Mysql
的。Mysql
的安装教程我参考的是这个:MySQL安装和配置教程(超详细版本)
安装好后,利用命令行或其他工具在MySQL中创建一个存储项目数据的数据库,方便之后使用QT用代码对数据库进行连接、建表和增删改查的操作:
Win+r
打开cmd:输入命令“mysql -u root -p”,按下回车键- 输入MySQL密码,按下回车键
- 创建数据库
create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则];
注意:不要忘记末尾的分号
二、通过ODBC连接MySQL数据库
官方解释:
ODBC(Open Database Connectivity,开放数据库互连)提供了一种标准的API(应用程序编程接口)方法来访问数据库管理系统(DBMS)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。ODBC的设计者们努力使它具有最大的独立性和开放性:与具体的编程语言无关,与具体的数据库系统无关,与具体的操作系统无关。
简单的说就是我的qt中含有ODBC的驱动:
所以利用ODBC去使用MySQL的数据库
2.1:下载ODBC
选择和qt编译器相同的字节比如我的qt使用的是64字节的
下载完成后,点卡下载下来的.msi
文件并运行:
下载一直点击next
:
选择custom
,表示自定义安装(以便修改安装位置)
最后一直点击next
然后再install
即可,等待安装好后即可。
安装好之后,关闭窗口,搜索ODBC
,并运行程序:
2.2:使用ODBC连接MySQL数据库
添加MySQL的DSN
红色的可以随便填,是自己对于ODBC驱动的描述,粉色的是MySQL的用户名和密码,数据库选择你在MySQL中创建的数据库名字即可:
点击Test
,测试是否能够成功连接;如果出现下图说明连接成功
然后点击应用
和确定
进行添加:
2.3:qt通过ODBC连接MySQL
现在main.cpp
中加入下面代码测试一下能不能在qt中连接成功:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("是你在ODBC中创建的Data source name");
db.setUserName("用户名");
db.setPassword("密码");
bool ok = db.open();
if (ok){
QMessageBox::information(this, "infor", "success");
}
else {
QMessageBox::information(this, "infor", "open failed");
qDebug()<<"error open database because"<<db.lastError().text();
}
Tips:这里可能会报错:VS2019 C1083 无法打开包括文件: “QSqlDatabase”
是vs里面项目配置的问题。
看到这个错误应该是没有dll,然后sql很容易想到数据库,所以在qt模块里添加,比较方便。
不出意外运行程序可以成功连接:
之后在主机进行安装时,除了打包程序,应该还需要安装对应的Mysql和ODBC
2.4:qt通过ODBC操作数据库
连接上数据库一般就是使用数据库,进行对数据库的增删改查。
这里有三种方法。
2.4.0:前言
🪧 首先单独建立一个头文件来处理数据库连接,如建立头文件connection.h
:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
static bool createConnection(){
//连接第一个数据库
//QMYSQL
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "connection1");//需要使用的数据库驱动和联检建立的名称(方便建立多个数据库连接【使用不同的数据库时】区分)
db.setHostName("127.0.0.1");//连接地址
db.setUserName("root");//数据库账户
db.setPassword("root");//密码
db.setPort(8889);//端口
//test_majiang.db
db.setDatabaseName("test_majiang");//需要用到的数据库
if (!db.open()) {//如果数据库连接失败,则弹出
//critical(QWidget *parent, const QString &title,
//const QString &text,
//QMessageBox::StandardButtons buttons = Ok,
//QMessageBox::StandardButton defaultButton = NoButton)
QMessageBox::critical(0, "Cannot open database",
"Unable to establish a database connection", QMessageBox::Cancel);
return false;
}
return true;
}
#endif // CONNECTION_H
如果需要移除一个数据库连接,可以使用:
QSqlDatabase::close();//关闭数据库
QSqlDatabase::removeDatabase();//移除该连接
在 Qt 中,QSqlDatabase::close() 和 QSqlDatabase::removeDatabase() 是用来关闭和移除数据库连接的。这两个函数通常一起使用,首先关闭数据库连接,然后移除该连接。这是为了确保资源被正确地释放。
以下是一个示例,展示了如何使用这两个函数:
#include <QSqlDatabase>
// 假设你已经创建了一个名为 "MyDbConnection" 的数据库连接
// 首先,关闭数据库连接
QSqlDatabase db = QSqlDatabase::database("MyDbConnection");
if (db.isOpen()) {
db.close();
}
// 然后,从连接池中移除该连接
QSqlDatabase::removeDatabase("MyDbConnection");
重要的是要注意,你应该只在确保没有其他 QSqlDatabase 对象使用同一个连接名时才移除该数据库连接。这是因为 QSqlDatabase 使用的是共享连接,即如果你有多个 QSqlDatabase 对象使用相同的连接名,它们实际上是在使用同一个连接。当最后一个使用该连接的 QSqlDatabase 对象被销毁或关闭时,连接才会被真正关闭。所以在调用 removeDatabase() 前要确保所有的 QSqlDatabase 对象都已经不再需要这个连接了
2.4.1:使用QSqlQuery
头文件
#include "connection.h"
#include <QVariant>
#include <QSqlDriver>
#include <QSqlRecord>
#include <QSqlField>
#include <QSqlError>
- 使用数据库前的准备:
//创建数据库连接
if(!createConnection()) return 1;//返回情况可以替换,视不同情况而定
//指定某个数据库连接
QSqlDatabase db2 = QSqlDatabase::database("connection1");
- 开始对数据进行操作:
首先创建QSqlQuery 对象,然后进行操作。
QSqlQuery query2(db2);
- 进行创表和插入值:
// qDebug() << "connection2:";
//创建表,并插入值
query2.exec("create table student (id int primary key,"
"name varchar(20))");
query2.exec("insert into student values(0, 'Mike')");
query2.exec("insert into student values(1, 'Lili')");
query2.exec("insert into student values(2, 'Jame')");
- where条件更新
//创建数据库连接(将结果写入数据库)
if (!createConnection()) return;
//指定某个数据库连接
QSqlDatabase db = QSqlDatabase::database("connection1");
QSqlQuery query(db);
query.prepare("UPDATE floor SET ActNutCnt = :ActNutCnt WHERE floorID = :floorID AND wtID = :wtID");
query.bindValue(":ActNutCnt", 20);
query.bindValue(":floorID", "F001");
query.bindValue(":wtID", "W001");
if (query.exec()) {
qDebug() << "Update successful";
}
else {
// 查询执行失败
QSqlError error = query.lastError();
QString errorMessage = error.text();
QMessageBox::about(NULL, "数据库错误", "检测结果写入失败:" + errorMessage);
}
// 关闭数据库连接
db.close();
- 批量处理
上面的单条插入语句明显比较麻烦,可以使用批量插入数据:
query2.exec("insert into student(id,name) values(3,'Qinsong')");//单挑操作
//名称绑定
query2.prepare("insert into student(id, name) values(:id, :name)");
int idValue = 4;
QString nameValue = "Songjiang";
query2.bindValue(":id", idValue);//绑定数据
query2.bindValue(":name", nameValue);
query2.exec();//执行
//位置绑定
query2.prepare("insert into student(id, name) values(?, ?)");
int idValue2 = 5;
QString nameValue2 = "LingChong";
query2.addBindValue(idValue2);//绑定数据
query2.addBindValue(nameValue2);
if(!query2.execBatch()) qDebug() <<"位置绑定:" <<query2.lastError() <<endl;//如果执行不成功执行
//批量处理
query2.prepare("insert into student(id, name) values(?,?)");
QVariantList ids;
ids << 6 << 7 << 8;
query2.addBindValue(ids);
QVariantList names;
names << "Qinghua" << "Nanda" << "Zhongkeda";
query2.addBindValue(names);
if(!query2.execBatch()) qDebug() << query2.lastError()<< endl;
- 进行查询并输出查询结果:
query2.exec("select * from student");//执行sql语句
while(query2.next()){
qDebug()<< query2.value(0).toInt() << query2.value(1).toString();
}
#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
// ...
QSqlQuery query;
// 以SELECT语句为例,查询你感兴趣的表和字段
if (query.exec("SELECT column1, column2 FROM your_table")) {
while (query.next()) {
// 使用value()方法获取字段值
QVariant column1 = query.value(0); // 0 是 column1 的索引
QVariant column2 = query.value(1); // 1 是 column2 的索引
// 你可以使用QVariant的转换函数,例如toInt(), toString()等来获取具体的类型
int intValue = column1.toInt();
QString stringValue = column2.toString();
// 根据你的需要处理这些数据
// ...
}
} else {
// 查询失败,你可以获取并处理错误信息
QSqlError error = query.lastError();
// 处理错误,例如打印错误消息
qDebug() << "Query failed:" << error.text();
}
- 查看数据驱动支持特性
查看当前数据库是否是支持某特性,比如当前记录的索引数(即结果条数):
int numRows;
if(db2.driver()->hasFeature(QSqlDriver::QuerySize)){//是否该特性
qDebug()<< "has feature:query size";
numRows = query2.size();
}else{
qDebug() << "no feature:query size";
query2.last();
numRows = query2.at() + 1;//使用at,需要之前使用quey2.next()遍历所有select搜索后的结果,而使用query2.size()则不需要
}
//此处执行上面的查询操作,下面的操作才有意义
qDebug() << "row number: " << numRows;
//指向索引为1的记录,即第二条记录
query2.seek(1);
//返回当前索引值
qDebug() << "current index:" << query2.at();
//获得当前行的记录
QSqlRecord record = query2.record();
//获得记录中"id"和"name"两个字段的值
int id = record.value("id").toInt();
QString name = record.value("name").toString();
qDebug() <<"id" << id << "name:" <<name;
//获得索引为1的字段,即第二个字段
QSqlField field = record.field(1);
//输出字段名和字段值,结果为"name"和"MaLiang"
qDebug() << "second field:" << field.name()
<< "field value:" << field.value().toString();
- 事务(使数据操作变为原子性)
如果中间有一步sql操作执行出错,则全部sql操作都不执行。
QSqlDatabase db2 = QSqlDatabase::database("connection2");
QSqlDatabase::database().transaction();//开始(类似于mutex线程锁)
QSqlQuery query(db2);//此语句必须在上面一条语句的后面
//执行sql操作
QSqlDatabase::database().commit();//结束
2.4.2:使用QSqlQueryModel查询模型
优势:
- 这是基于sql查询的只读模型,编写sql语句变得容易。
文件头:
#include <QSqlQueryModel>
核心代码:
QSqlDatabase db = QSqlDatabase::database("connection1");
QSqlQueryModel *model = new QSqlQueryModel(this);
model->setQuery("select * from student", db);
model->setHeaderData(0, Qt::Horizontal, tr("学号"));
model->setHeaderData(1, Qt::Horizontal, tr("姓名"));
model->setHeaderData(2, Qt::Horizontal, tr("课程"));
QTableView *view = new QTableView(this);
view->setModel(model);
setCentralWidget(view);
2.4.3:使用QSqlTableModel表格模型⭐
先上结果
优势:
-
编译的代码很容易适应其他的数据源,例如后面如果要使用xml文件来存储数据,只需要更换数据模型。
-
提供了一次只能操作一个sql表的读/写模型,可以浏览和修改独立的sql表,并且只需编写很少的代码,无需了解sql语句。
-
1 准备
头文件:
#include <QSqlTableModel>
QSqlTableModel* model;//创建对象指针
- 2 进行操作:
QSqlDatabase db = QSqlDatabase::database("connection1");
model = new QSqlTableModel(this, db);//由于在窗口的类中创建对象,因此实例化对象时,使用this指针(指向操作函数的指针)作为父对象
model->setTable("student");
model->select();//执行
//设置编辑策略
model->setEditStrategy(QSqlTableModel::OnManualSubmit);//对所有模型改变立即用到数据库
ui->tableView->setModel(model);
-
UI设计:
-
对应槽函数:
// 提交修改按钮
void MainWindow::on_pushButton_clicked()
{
// 开始事务操作
model->database().transaction();
if (model->submitAll()) {
if(model->database().commit()) // 提交
QMessageBox::information(this, tr("tableModel"),
tr("数据修改成功!"));
} else {
model->database().rollback(); // 回滚
QMessageBox::warning(this, tr("tableModel"),
tr("数据库错误: %1").arg(model->lastError().text()),
QMessageBox::Ok);
}
}
// 撤销修改按钮
void MainWindow::on_pushButton_2_clicked()
{
model->revertAll();
}
// 查询按钮,进行筛选
void MainWindow::on_pushButton_5_clicked()
{
QString name = ui->lineEdit->text();
// 根据姓名进行筛选,一定要使用单引号
model->setFilter(QString("name = '%1'").arg(name));
model->select();
}
// 显示全表按钮
void MainWindow::on_pushButton_6_clicked()
{
model->setTable("student");
model->select();
}
// 按id升序排列按钮
void MainWindow::on_pushButton_7_clicked()
{
//id字段,即第0列,升序排列
model->setSort(0, Qt::AscendingOrder);
model->select();
}
// 按id降序排列按钮
void MainWindow::on_pushButton_8_clicked()
{
model->setSort(0, Qt::DescendingOrder);
model->select();
}
// 删除选中行按钮
void MainWindow::on_pushButton_4_clicked()
{
// 获取选中的行
int curRow = ui->tableView->currentIndex().row();
// 删除该行
model->removeRow(curRow);
int ok = QMessageBox::warning(this,tr("删除当前行!"),
tr("你确定删除当前行吗?"), QMessageBox::Yes, QMessageBox::No);
if(ok == QMessageBox::No)
{ // 如果不删除,则撤销
model->revertAll();
} else { // 否则提交,在数据库中删除该行
model->submitAll();
}
}
// 添加记录按钮
void MainWindow::on_pushButton_3_clicked()
{
// 获得表的行数
int rowNum = model->rowCount();
int id = 10;
// 添加一行
model->insertRow(rowNum);
model->setData(model->index(rowNum, 0), id);
// 可以直接提交
//model->submitAll();
}
三、项目实战
connection.h
是负责连接数据库和创建表的头文件:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
#pragma execution_character_set("utf-8");
static bool createConnection() {
//连接第一个数据库
//QMYSQL
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "connection1");//需要使用的数据库驱动和联检建立的名称(方便建立多个数据库连接【使用不同的数据库时】区分)
db.setHostName("127.0.0.1");//连接地址
db.setUserName("root");//数据库账户
db.setPassword("55667788");//密码
db.setPort(3306);//端口
//test.db
db.setDatabaseName("test");//需要用到的数据库
if (!db.open()) {//如果数据库连接失败,则弹出
//critical(QWidget *parent, const QString &title,
//const QString &text,
//QMessageBox::StandardButtons buttons = Ok,
//QMessageBox::StandardButton defaultButton = NoButton)
QMessageBox::critical(0, "Cannot open database",
"Unable to establish a database connection", QMessageBox::Cancel);
return false;
}
else {
QMessageBox::information(NULL, "infor", "link success");
}
//下面来创建表
//如果MySQL数据库中已经存在同名的表,则下面代码不会执行
QSqlQuery query2(db);
// qDebug() << "connection2:";
//创建表,并插入值
query2.exec("create table student (id int primary key,"
"name varchar(20))");
query2.exec("insert into student values(0, 'Mike')");
query2.exec("insert into student values(1, 'Lili')");
query2.exec("insert into student values(2, 'Jame')");
}
#endif // CONNECTION_H
Admin
是操作数据库的管理界面:
Admin.h
:
#pragma once
#include <QMainWindow>
#include "ui_Admin.h"
#include <QSqlTableModel>
#pragma execution_character_set("utf-8");
class Admin : public QMainWindow
{
Q_OBJECT
public:
Admin(QWidget *parent = nullptr);
~Admin();
QSqlTableModel* model;//创建对象指针
private:
Ui::AdminClass ui;
private slots:
void on_add_clicked();
void on_modify_clicked();
void on_del_clicked();
void on_rollback_clicked();
void on_show_all_clicked();
};
Admin.cpp
:
#include "Admin.h"
#include <qmessagebox.h>
#include <QSqlDatabase>
#include <QMessageBox>
#include <qsqlerror.h>
#include "connection.h"
#include <QSqlTableModel>
Admin::Admin(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
if (!createConnection()) {
return;
}
QSqlDatabase db = QSqlDatabase::database("connection1");
model = new QSqlTableModel(this, db);//由于在窗口的类中创建对象,因此实例化对象时,使用this指针(指向操作函数的指针)作为父对象
model->setTable("student");
model->select();//执行
QTableView* view = new QTableView;
view->setModel(model);
// 你可以根据你数据库表的实际列名进行设置
QHeaderView* header = view->horizontalHeader();
header->setModel(model);
header->setSectionResizeMode(QHeaderView::Stretch);
// 设置显示名称,从0开始计数
model->setHeaderData(0, Qt::Horizontal, tr("列名1"));
model->setHeaderData(1, Qt::Horizontal, tr("列名2"));
model->setHeaderData(2, Qt::Horizontal, tr("列名3"));
//设置编辑策略
model->setEditStrategy(QSqlTableModel::OnManualSubmit);//对所有模型改变立即用到数据库
ui.tableView->setModel(model);
}
Admin::~Admin()
{}
// 添加记录按钮
void Admin::on_add_clicked()
{
// 获得表的行数
int rowNum = model->rowCount();
int id = 10;
// 添加一行
model->insertRow(rowNum);
model->setData(model->index(rowNum, 0), id);
// 可以直接提交
//model->submitAll();
}
// 删除选中行按钮
void Admin::on_del_clicked()
{
// 获取选中的行
int curRow = ui.tableView->currentIndex().row();
// 删除该行
model->removeRow(curRow);
int ok = QMessageBox::warning(this, tr("删除当前行!"),
tr("你确定删除当前行吗?"), QMessageBox::Yes, QMessageBox::No);
if (ok == QMessageBox::No)
{ // 如果不删除,则撤销
model->revertAll();
}
else { // 否则提交,在数据库中删除该行
model->submitAll();
}
}
// 撤销修改按钮
void Admin::on_rollback_clicked()
{
model->revertAll();
}
// 提交修改按钮
void Admin::on_modify_clicked()
{
// 开始事务操作
model->database().transaction();
if (model->submitAll()) {
if (model->database().commit()) // 提交
QMessageBox::information(this, tr("tableModel"),
tr("数据修改成功!"));
}
else {
model->database().rollback(); // 回滚
QMessageBox::warning(this, tr("tableModel"),
tr("数据库错误: %1").arg(model->lastError().text()),
QMessageBox::Ok);
}
}
// 显示全表按钮
void Admin::on_show_all_clicked() {
model->setTable("student");
model->select();
}
功能正常: