参考b站:视频连接
新建一个c++class,sqlmange,并且在.pro文件中添加上sql

使用c++单例模式:
它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

该代码实现了 SQLManange 类,该类可以用于管理数据库连接和操作。代码使用了单例模式,确保只有一个 SQLManange 实例存在,并提供了一些基本功能,例如测试数据库连接和操作。我不是很懂这部分先更着敲
#ifndef SQLMANGE_H
#define SQLMANGE_H
#include
// sqlmange 类声明
class sqlmange
{
public:
// 构造函数
sqlmange();
// 单例模式:获取类唯一实例
static sqlmange* getInstance();
// 测试函数
void test();
private:
// 私有构造函数,禁止外部直接创建对象
sqlmange(const sqlmange&) = delete;
// 私有赋值运算符,禁止外部直接赋值
sqlmange& operator=(const sqlmange&) = delete;
// 单例模式:类唯一实例指针
static sqlmange* instance;
};
#endif // SQLMANGE_H
#include "sqlmange.h"
// sqlmange 类唯一实例指针初始化
sqlmange* sqlmange::instance = nullptr;
// sqlmange 类构造函数实现
sqlmange::sqlmange() {}
// sqlmange 类获取实例函数实现
// 如果实例不存在,则创建新实例并赋值给 instance 指针
// 然后返回 instance 指针
sqlmange* sqlmange::getInstance()
{
if (nullptr == instance) {
instance = new sqlmange();
}
return instance;
}
// sqlmange 类测试函数实现
// 输出 "test" 到调试信息
void sqlmange::test()
{
qDebug() << "test";
}
在main函数中使用sqlmange::getInstance()->test();调用写好的接口
// 构造函数
sqlmange();
// 单例模式:获取类唯一实例
static sqlmange* getInstance();
//初始化数据库
void init();
//登陆
bool login(QString strUsername ,QString strPassword);
//获取所有用户
QVector<QStringList> getUsers(QString strCondition = "");
//添加用户
void AddUser(QVector<QStringList>);
//删除用户
void DelUser(QString strID);
//获取所有图书
QVector<QStringList> getBooks(QString strCondition = "");
//增加图书
void AddBook(QVector<QStringList>);
//修改图书
void ModBook(QString strID);
//删除图书
void DelBook(QString strID);
//图书归还,谁还的,还的什么书
QString ReturnBook(QString strUserID, QString strBookID);
//图书借阅,谁借的,借的什么书
QString BorrowBook(QString strUserID, QString strBookID);
//获取借阅记录
QVector<QStringList> getRecords(QString strCondition = "");
//清空记录
QString clearRecord();
使用QSqlDatabase库里的函数,官方给我们提供了参考

将之前写好的数据库放到应用程序所在目录下的db文件夹,编写代码
void sqlmange::init()
{
// 打开数据库
// 使用 QSQLITE 数据库驱动
m_db = QSqlDatabase::addDatabase("QSQLITE");
// 设置数据库名称
// 数据库文件位于应用程序目录下的 db/book.db 文件
m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/db/book.db");
// 打开数据库
// 并输出打开结果到调试信息
qDebug() << m_db.open();
}
此时发现没有驱动,排查发现是此时程序运行在打包好的路径里了,没有把现在用到的数据库打包上,所以我们回到bin文件夹把之前打包的库都删了就能够运行。
找库的路径都是优先当前程序所在路径


使用查询语句查询用户名和密码是否对上即可
bool sqlmange::login(QString strUsername, QString strPassword)
{
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("select * from user where username = '%1' and password = '%2'").arg(strUsername).arg(strPassword);
// 执行 SQL 查询
bool ret = q.exec(strSql);
// 如果查询失败,输出错误信息
if(!ret)
{
qDebug()<<q.lastError().text();
}
// 返回查询结果
return ret;
}
// 使用单例实例执行登录操作,并输出登录结果到调试信息
qDebug() <<"login:"<< sqlmange::getInstance()->login("xiaoming","123456");
把 where后边的语句都用做条件,如where username like '%x' or nickname '小'
QVector<QStringList> sqlmange::getUsers(QString strCondition)
{
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
// 使用 strCondition 作为查询条件
QString strSql = QString("select * from user %1").arg(strCondition);
// 存储查询结果的容器
QVector<QStringList> vec;
// 执行 SQL 查询
bool ret = q.exec(strSql);
// 如果查询失败,输出错误信息
if(!ret)
{
qDebug()<<q.lastError().text();
}
else
{
// 获取查询结果的列数
int iCols = q.record().count();
// 临时存储每行的查询结果
QStringList l;
// 遍历查询结果
while(q.next())
{
// 遍历每一列
for (int i = 0;i< iCols;i++)
{
// 将当前列的值添加到临时列表中
l<<q.value(i).toString();
}
// 将临时列表添加到最终结果容器中
vec.push_back(l);
// 清空临时列表
l.clear();
}
}
// 返回查询结果
return vec;
}
在mian里使用qDebug() <<"getuser:"<< sqlmange::getInstance()->getUsers("where username like '%xiao%' or nickname like '%小%'");
首先从数据库里读出数据展示在table view中,数据存在容器中相当于一个二维数组
void Cell_Usermange::initPage(QString strCondition)
{
//查询数据库并且显示
QVector<QStringList> l = sqlmange::getInstance()->getUsers(strCondition);
// 清空之前的数据,以便显示新的查询结果
m_model.clear();
// 设置表格列头标签
m_model.setHorizontalHeaderLabels(QStringList{"用户ID", "姓名", "年级", "权限","所属学院", "用户名", "密码"});
for(int i = 0;i<l.size();i++)
{
// 创建一个新的行
QList<QStandardItem *> items;
// 遍历每一列
for(int j = 0;j<l[i].size();j++)
{
// 创建一个新的单元格项并设置文本
items.append(new QStandardItem(l[i][j]));
}
// 将行添加到表格模型中
m_model.appendRow(items);
}
}
让页面切换时调用initpage函数
if (str == "btn_user") {
// 显示用户管理页面
m_userpage->initPage("");
ui->stackedWidget->setCurrentIndex(0);
break;
}
先在cell—main里修改样式,添加一个按钮按下的状态 QPushButton[name = “btn”]:press{background: #3a8ee6;}
首先需要在sqlmange里把删除的逻辑写好
void sqlmange::DelUser(QString strID)
{
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("delete from user where userid = '%1'").arg(strID);
// 执行 SQL 查询
q.exec(strSql);
}
然后我们再去编写点击按钮执行删除的操作,使用选中行的id,执行删除这个userid的行
void Cell_Usermange::on_btn_del_clicked()
{
// 删除用户
// 获取当前选中的行号
int r = ui->tableView->currentIndex().row();
// 判断是否选中行
if (r < 0) {
// 弹出错误提示框
QMessageBox::information(nullptr, "错误", "没有选中用户");
} else {
// 获取选中行的用户ID,text变为QString
QString id = m_model.item(r, 0)->text();
// 使用 SQLManange 类删除用户
sqlmange::getInstance()->DelUser(id);
// 弹出成功提示框
QMessageBox::information(nullptr, "成功", "删除成功");
// 刷新页面
initPage();
}
}
右击文编框,转到槽,使用textchanged槽

查询官方文档,有以下的信号,文本改变时text直接就是我们输入的文本

我们使用字符串拼接来达到搜索的目的,getuser里已经是select * from user %1,我们把这%1替换为模糊搜索语句,也就是最后运行sql语句是select * from user where username like '%1%' or nickname like '%2%',这样很方便
void Cell_Usermange::on_le_search_textChanged(const QString &text)
{
//搜索用户
//使用字符串拼接来达到模糊搜索
QString strCond = QString("where username like '%1%' or nickname like '%2%'").arg(text).arg(text);
initPage(strCond);
}
使用qFile读取csv文件,把里边的数据用“,”划分存储到容器vector中,在使用insert将容器里的数据导入数据库。此时写入数据库的代码是乱码,应为csv是gbk编码,数据是是utf-8编码,所以需要转换(暂未解决)

void sqlmange::AddUser(QVector<QStringList> addData)
{
// 开启数据库事务
m_db.transaction();
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 遍历用户数据
for (int i = 0; i < addData.size(); i++)
{
// 构建 SQL 插入语句
// 使用占位符防止 SQL 注入攻击
// 将字符串转换为 UTF-8 编码,避免中文乱码
QString strSql = QString("insert into user VALUES(NULL,'%1','%2','%3','%4','%5','%6')").arg(addData[i][0].toUtf8()).arg(addData[i][1].toUtf8()).arg(addData[i][2].toUtf8()).arg(addData[i][3].toUtf8()).arg(addData[i][4].toUtf8()).arg(addData[i][5].toUtf8());
// 执行 SQL 插入
q.exec(strSql);
}
// 提交数据库事务
m_db.commit();
}
void Cell_Usermange::on_btn_import_clicked()
{
//导入用户并显示
// 打开文件选择对话框
QString strPath = QFileDialog::getOpenFileName(nullptr,"请输入文件路径");
if(!strPath.isEmpty())
{
// 打开 CSV 文件
QFile f(strPath);
f.open(QFile::ReadOnly|QFile::Text);
// 定义一个用于存储用户数据的容器
QVector<QStringList> addData;
while(!f.atEnd())
{
// 读取一行数据
QString str = f.readLine();
// 将一行数据分割成字符串列表
QStringList l = str.split(",");
// 判断列数是否正确
if (l.size() != 6)
{
// 弹出错误提示框
QMessageBox::information(nullptr, "错误", "导入失败,列数对不上");
return;
}
// 删除字符串末尾的换行符
l[l.size() - 1].chop(2);
// 将数据添加到容器中
addData.push_back(l);
}
// 弹出成功提示框
QMessageBox::information(nullptr, "成功", "导入成功");
// 使用 SQLManange 类添加用户
sqlmange::getInstance()->AddUser(addData);
// 刷新页面
initPage();
}
}
乱码问题暂时还不会解决
代码和初始化用户接口一样,getbooks修改sql语句改为book,initpage一样,随后在main。cpp里加上m_bookpage->initPage("");展示记录
## 4.2 搜索接口
拼接sql语句实现查询
void Cell\_BookManger::on\_le\_search\_textChanged(const QString &arg1)
{
//搜索图书
//使用字符串拼接来达到模糊搜索
QString strCond = QString("where name like '%1%' or type1 like '%2%' or type2 like '%3%' or type3 like '%4%'").arg(arg1).arg(arg1).arg(arg1).arg(arg1);
initPage(strCond);
}
和删除用户接口一样
图书的增加和修改需要点击是在新弹出一个界面来输入
新建一个qt界面设计师类,选择withoutbutton,搭建下图所示界面


取消按钮的代码很简单,直接this->hide()隐藏了就行
先编写sql的逻辑代码,一个是修改一个是插入(查百度即可)在套上之前写好的模板。
void sqlmange::AddBook(QVector<QStringList> vec)
{
//增加图书
// 创建 SQL 查询对象
QSqlQuery q(m_db);
if(vec.size() == 0)
{
return;
}
QStringList ldata = vec[0];
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("INSERT INTO book"
"values(NULL,'%1','%2','%3','%4','%5','%6','')")
.arg(ldata[1])
.arg(ldata[2])
.arg(ldata[3])
.arg(ldata[4])
.arg(ldata[5])
.arg(ldata[6]);
// 执行 SQL 查询
q.exec(strSql);
}
void sqlmange::ModBook(QStringList ldata)
{
//更新图书
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("INSERT INTO book "
"values(NULL,'%1','%2','%3','%4','%5',%6,'')")
.arg(ldata[1])
.arg(ldata[2])
.arg(ldata[4])
.arg(ldata[5])
.arg(ldata[6])
.arg(ldata[3]);
// 执行 SQL 查询
q.exec(strSql);
}
在编写确定按钮的槽函数,通过UI界面那要需要修改的信息,通过一个判断将修改和增加写到一起
// 函数声明:设置书籍ID
void Dlg_BookAdd_Update::setType(int l)
{
// 将参数l赋值给成员变量m_id
m_id = l;
}
// 函数声明:确认按钮槽函数
void Dlg_BookAdd_Update::on_btn_ok_clicked()
{
// 定义一个QStringList用于存储书籍信息
QStringList l;
// 将书籍ID转换为QString并添加到l中
l << QString::number(m_id);
// 将书名、价格、数量、分类1、分类2、分类3添加到l中
l << ui->le_name->text();
l << ui->le_press->text();
l << ui->le_count->text();
l << ui->cb1->currentText();
l << ui->cb2->currentText();
l << ui->cb3->currentText();
l << "";
// 判断m_id是否为-1
if (-1 != m_id) {
// 更新书籍信息
sqlmange::getInstance()->ModBook(l);
} else {
// 添加新书籍
QVector<QStringList> vec;
vec.push_back(l);
sqlmange::getInstance()->AddBook(vec);
}
}
回到图书管理界面,导入更新图书的头文件,执行代码,id默认是-1所以是执行添加图书的操作,最后刷新界面展示记录
void Cell_BookManger::on_btn_add_clicked()
{
//添加图书
Dlg_BookAdd_Update dlg;
dlg.exec();
initPage();
}
此时发现并没有添加进去,调试发现sql语句有问题,book后边没有空格,同时从需要将输入数据和数据库里的顺序一一对应,不然就无法加入

sql语句,UPDATE修改属性,注意语句的空格
void sqlmange::ModBook(QStringList ldata)
{
//更新图书
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("UPDATE book "
"set name = '%1',press = '%2',type1 = '%3',type2 = '%4',type3 = '%5',count = '%6' "
"where bookid = '%7'")
.arg(ldata[1])
.arg(ldata[2])
.arg(ldata[4])
.arg(ldata[5])
.arg(ldata[6])
.arg(ldata[3])
.arg(ldata[0]);
// 执行 SQL 查询
q.exec(strSql);
}
此时所有信息都要自己填入,我们需要在修改的时候让它自动显示原来的信息。不管是修改还是新增都获取原来图书的信息,如果有信息就展示
void Dlg_BookAdd_Update::setType(int id)
{
// 将参数l赋值给成员变量m_id
m_id = id;
//获取当前选中行的图书信息
QVector<QStringList> l = sqlmange::getInstance()->getBooks(QString("where bookid = %1 ").arg(m_id));
if(l.size() == 1)
{
QStringList data = l[0];
//使用set将文本填入到窗口
ui->le_name->setText(data[1]);
ui->le_press->setText(data[2]);
ui->cb1->setCurrentText(data[3]);
ui->cb2->setCurrentText(data[4]);
ui->cb3->setCurrentText(data[5]);
ui->le_count->setText(data[6]);
}
}
新建一个qt界面,按下图搭建

先编写图书管理界面点击借阅的按钮,判断是否选中图书,选中开始执行,同时在进入对话框之前查询图书的数量是否为0,为0就失败
void Cell_BookManger::on_btn_borrow_clicked()
{
//借阅图书
// 获取当前选中的行号
int r = ui->tableView->currentIndex().row();
// 判断是否选中行
if (r < 0) {
// 弹出错误提示框
QMessageBox::information(nullptr, "错误", "没有选中图书");
}
else
{
// 获取选中行的图书ID,text变为QString
QString id = m_model.item(r, 0)->text();
//获取数量
QString count = m_model.item(r, 6)->text();
if(count.toInt() <= 0 )
{
QMessageBox::information(nullptr, "信息", "借阅失败,图书数量不足");
return;
}
//执行借阅
Dlg_book_bor dlg;
//传入bookid
dlg.setBookID(id.toInt());
int ret = dlg.exec();
// 弹出成功提示框
QMessageBox::information(nullptr, "信息", ret?"借阅成功":"借阅失败");
// 刷新页面
initPage();
}
}
同时需要拿到userid,使用实参传递直接修改传进去的userid
bool sqlmange::login(QString strUsername, QString strPassword,int &userid)
{
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 使用占位符防止 SQL 注入攻击
QString strSql = QString("select * from user where username = '%1' and password = '%2'").arg(strUsername).arg(strPassword);
// 执行 SQL 查询
bool ret = q.exec(strSql);
// 如果查询失败,输出错误信息
if(!ret)
{
qDebug()<<q.lastError().text();
}
else
{
//拿到用户id
q.next();
userid << q.value(0).toInt();
}
// 返回查询结果
return ret;
编写输入用户名和密码后的代码,调用login,获得userid和bookid,最后调用借阅书本的代码
void Dlg_book_bor::on_btn_ok_clicked()
{
// 判断用户名密码是否正确
do
{
// 获取用户名和密码
QString username = ui->le_username->text();
QString password = ui->le_password->text();
// 用户 ID
int m_userid = 0;
// 登录验证
bool ret = sqlmange::getInstance()->login(username, password, m_userid);
// 登录失败
if (!ret)
{
// 关闭对话框
this->done(0);
// 退出循环
break;
}
// 生成借阅记录
sqlmange::getInstance()->BorrowBook(QString::number(m_userid), QString::number(m_bookid));
// 关闭对话框并返回成功状态
this->done(1);
} while (false);
}
最后我们只需要写sql语句就可以,并且添加插入记录的sql语句,QDateTime可以获取当前的时间
QString sqlmange::BorrowBook(QString strUserID, QString strBookID)
{
//借阅图书
// 创建 SQL 查询对象
QSqlQuery q(m_db);
// 构建 SQL 查询语句
// 查询书本数量-1
QString strSql1 = QString("UPDATE book set count = count-1 where bookid = '%1'").arg(strBookID);
//添加记录
QString strSql2 = QString("INSERT into record VALUES(NULL,'%1','%2','%3','%4','')")
.arg(strBookID)
.arg(strUserID)
.arg(QDateTime::currentSecsSinceEpoch())
.arg(QDateTime::currentSecsSinceEpoch()+3600*24*10);
bool ret1 = q.exec(strSql1);
if(!ret1)
{
qDebug()<<q.lastError().text();
}
bool ret2 = q.exec(strSql2);
if(!ret2)
{
qDebug()<<q.lastError().text();
}
return QString("");
}
小功能,在借阅的时候显示图书的名称
void Dlg_book_bor::setBookID(int id)
{
m_bookid = id;
//查询对于id的书本名
auto l = sqlmange::getInstance()->getBooks(QString("where bookid = %1").arg(m_bookid));
//显示在label上
ui->lb_bookname->setText(QString("图书名称: "+l[0][1]));
}
代码和初始化用户接口一样,getrecords修改sql语句改为record,initpage一样,随后在main。cpp里加上m_recordpage->initPage("");展示记录
调用的借阅的界面,代码更图书借阅类似
void Cell_RecordMange::on_btn_return_clicked()
{
//借阅图书
// 获取当前选中的行号
int r = ui->tableView->currentIndex().row();
// 判断是否选中行
if (r < 0) {
// 弹出错误提示框
QMessageBox::information(nullptr, "错误", "没有选中图书");
}
else
{
// 获取选中行的图书ID,text变为QString
QString id = m_model.item(r, 0)->text();
//执行借阅
Dlg_book_bor dlg;
//传入bookid
dlg.setBookID(id.toInt());
dlg.setType(true);
int ret = dlg.exec();
// 弹出成功提示框
QMessageBox::information(nullptr, "信息", ret?"归还成功":"归还失败,账号或者密码错误");
// 刷新页面
initPage();
}
}