Qt之数据库使用(十四)

server/2024/12/29 9:51:33/

Qt开发 系列文章 - QSqlDatabase-SQLite(十四)


目录

前言

一、SQLite

二、SQLite使用

1.添加SQL

2.创建数据库

3.定义类及相关变量

4.相关功能函数

5.用户类定义

6.用户类使用

7.效果演示

8.SQLite数据库

总结


前言

‌Qt支持的数据库包括SQLite、MySQL、PostgreSQL、ODBC等‌。其中,SQLite是Qt默认支持的数据库,无需额外的驱动就可以使用,适合轻量级的应用,不需要多用户、大数据量的场景。对于大型应用,MySQL和PostgreSQL则是更好的选择,它们提供了更强大的数据处理能力‌。本文将讲解SQLite的设计使用。


一、SQLite

SQLite是一款轻量级的嵌入式数据库,它的数据库就是一个文件,可以直接通过Qt的SQL模块操作。SQLite的优势在于无需配置、轻量级,适合不需要多用户、大数据量的场景‌。在Qt中使用SQLite时,可以通过QSqlDatabase类来创建一个数据库连接,使用addDatabase()函数指定数据库类型为"QSQLITE",然后设置数据库名字并打开数据库‌ 。

二、SQLite使用

Qt中对SQLite的使用,通常需要用到以下几个相关类:

  1. QSqlDatabase类:数据库连接类,表示一个数据库连接;
  2. QSqlQuery类:数据库操作类,可以操作SQL语句;
  3. QSqlError类:数据库错误信息类,用户收集数据库底层传递到Qt中的错误信息。

下面将讲解SQLite在Qt中的使用。

1.添加SQL

想使用SQL,首先需要先在.pro项目配置文件中添加sql模块,如下所示。

QT       += sql

2.创建数据库

在原有的Qt项目上新建一个类,用于专门操作数据库的,并将该类属于工程项目主窗口的私有变量(当然你也可以不创建该类,直接在项目在构造函数上定义该数据库,进行使用)。

在Qt原有项目怎样新建一个类,在博主以前的博文有详细的介绍,这里本文不做介绍,将设计在新建的类的构造函数处定义数据变量,具体有以下步骤。

  1. 先打印出,电脑所装Qt支持的数据库种类;
  2. 创建数据库文件的路径及名称;
  3. 添加SQLite驱动,创建连接;
  4. 看SQLite是否存在,若不存在,自动创建;若存在,在已有的数据库上进行;
  5. 打开创建的数据库;
  6. 开启事务,准备进行操作;
  7. 创建数据库操作类QSqlQuery;
  8. 在数据库上创建若干表格;
  9. 提交事务到数据库,并保存更改。

具体实现,代码如下(示例):

void testhread::InitQSqlite()
{//**0**打印Qt支持的数据库qDebug() << "QSqlDatabase=" << QSqlDatabase::drivers();//**1**创建数据库名称及路径QTime systime = QTime::currentTime();QString saveFileFolder = QApplication::applicationDirPath()+"/savefile/";QDir folder; //创建文件夹if(!folder.exists(saveFileFolder))folder.mkpath(saveFileFolder); //不存在则创建文件夹QString DBFileName = "dbmsg" + systime.toString("hh-mm-ss") + ".db";QString DBFilePath = saveFileFolder + DBFileName;//**2**添加SQLite驱动 创建连接m_db = QSqlDatabase::addDatabase("QSQLITE");//**3**SQLite不存在,自动创建;已经存在,在已有的数据库上进行。m_db.setDatabaseName(DBFilePath);//**4**打开数据库if(!m_db.open()){qDebug() << "Failed to open database!" << m_db.lastError().text();return;} elseqInfo() << "Successfully opened database! SQLite Type.";//**5**开始事务if (!m_db.transaction()) {qDebug() << "Failed to begin transaction";return;}//**6**创建数据库QSqlQuerysql = new QSqlQuery(m_db);//**7**在数据库上创建表格-studentQString createSql = QString("CREATE TABLE IF NOT EXISTS student (\id INT PRIMARY KEY NOT NULL,name TEXT NOT NULL,\age INT NOT NULL,height INT NOT NULL)");if(!sql->exec(createSql))qDebug() << "Error: Fail to create student table. " << sql->lastError();elseqDebug() << "student table create successful!";//在数据库上创建表格-状态反馈createSql = QString("CREATE TABLE IF NOT EXISTS MachineStatus(\Systime INTEGER (4) PRIMARY KEY,stat INTEGER (1),MainVer INTEGER (1),SubVer INTEGER (1),\Type INTEGER (1),Broad INTEGER (1),Online INTEGER (1))");if(!sql->exec(createSql))qDebug() << QString("Fail to create MachineStatus table.")<< sql->lastError();elseqDebug() << "MachineStatus table create successful!";//**8**提交事务并保存更改if (!m_db.commit()) {qDebug() << "Failed to commit transaction";return;}
}

3.定义类及相关变量

上一步我们在原有的Qt项目上新建一个类,用于专门操作数据库的,这里来定义该类。具体实现,代码如下(示例):

#ifndef TESTHREAD_H
#define TESTHREAD_H#include <QThread>
#include <QDebug>
#include <QByteArray>
#include <QTableView>
#include <QSqlQueryModel>
#include <QSqlDatabase>
#include <QSqlError>
#include <QFile>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QTime>
#include <QDir>
#include "qstandarditemmodel.h"#define MAXROWCNT 20
#define MAXROWHIG 4
#define MAXCOLWID 79
//状态反馈信息
typedef struct
{qint32 Systime;quint8 stat, MainVer, SubVer, Type, Broad;quint8 Online;
}FK_SatFBInfo_struct;class testhread : public QThread
{Q_OBJECT
public:testhread();~testhread(){}QTableView *UNKNTableView;FK_SatFBInfo_struct *FKSatFBInfo;QSqlDatabase m_db;           //数据库对象QSqlQuery *sql;bool g_flag;bool pushmsg_flag;void InitTableView(void);
protected:void InitQSqlite(void);void run(void);void ShowFKSFInfo(FK_SatFBInfo_struct *pFKSF);
private:QStandardItemModel* m_Model;
};
#endif // TESTHREAD_H

4.相关功能函数

相关功能函数主要包括构造函数(实现变量及数据库初始化)、线程run()函数。

这里讲解下,采用线程实现的方式,初始化(定义好)数据库文件后,然后通过UI界面上来控制线程启动,和决定是否向数据库不间断地插入数据,完成数据保存到数据库文件上,最后还通过UI界面表格将数据库的数据刷到表格上。至于为啥这样设计实现,效率更高更快,一般在网络、工业上数据处理均采取这样的方式。

具体实现,代码如下(示例):

#include "testhread.h"
testhread::testhread()
{qRegisterMetaType<QVector<int>>("QVector<int");qRegisterMetaTypeStreamOperators<QVector<int>>();g_flag = false;pushmsg_flag = false;memset(&FKSatFBInfo,0,sizeof(FKSatFBInfo));InitQSqlite();
}
void testhread::InitTableView(void)
{m_Model = new QStandardItemModel;QStringList list;list << QString("Time")<<"运行状态"<<"主版本号"<<"子版本号"<<"项目类型"<<"板上自检"<<"在线状态";// 设置表格行数和列数m_Model = new QStandardItemModel(MAXROWCNT, list.size(), this);// 设置表头m_Model->setHorizontalHeaderLabels(list);UNKNTableView->setModel(m_Model);//UNKNTableView->setAutoScroll(true);UNKNTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);for(quint8 i= 0; i<list.size(); i++)UNKNTableView->setColumnWidth(i, MAXCOLWID);for(quint16 i= 0; i<MAXROWCNT; i++)UNKNTableView->setRowHeight(i, MAXROWHIG);
}
void testhread::run()
{FK_SatFBInfo_struct FKSatFBInfo;while(g_flag){if (pushmsg_flag) {//插入表格QString tempinfo = QString("INSERT INTO MachineStatus(Systime,stat,MainVer,""SubVer,Type,Broad,Online) VALUES(?,?,?,?,?,?,?)");sql->prepare(tempinfo);FKSatFBInfo.Systime += 500;FKSatFBInfo.stat = 3;FKSatFBInfo.MainVer = 1;FKSatFBInfo.SubVer = 2;FKSatFBInfo.Type = 0;FKSatFBInfo.Broad = 0;FKSatFBInfo.Online = 1;sql->addBindValue(FKSatFBInfo.Systime);sql->addBindValue(FKSatFBInfo.stat);sql->addBindValue(FKSatFBInfo.MainVer);sql->addBindValue(FKSatFBInfo.SubVer);sql->addBindValue(FKSatFBInfo.Type);sql->addBindValue(FKSatFBInfo.Broad);sql->addBindValue(FKSatFBInfo.Online);if(!sql->exec())qDebug() << "Fail to insert data. " << sql->lastError().text();ShowFKSFInfo(&FKSatFBInfo);}msleep(500);}
}void testhread::ShowFKSFInfo(FK_SatFBInfo_struct *pFKSF)
{static quint16 cnt=0;if(cnt == (MAXROWCNT)){for(quint16 i=0;i<MAXROWCNT;i++){for(quint8 j=0;j<7;j++){auto tempIndex = UNKNTableView->model()->index(i, j);m_Model->clearItemData(tempIndex);}}cnt = 0;}m_Model->setData(m_Model->index(cnt, 0), pFKSF->Systime);m_Model->setData(m_Model->index(cnt, 1), "空闲态0");m_Model->setData(m_Model->index(cnt, 2), pFKSF->MainVer);m_Model->setData(m_Model->index(cnt, 3), pFKSF->SubVer);m_Model->setData(m_Model->index(cnt, 4), pFKSF->Type);m_Model->setData(m_Model->index(cnt, 5), pFKSF->Broad);m_Model->setData(m_Model->index(cnt, 6), pFKSF->Online);auto lastModelIndex = UNKNTableView->model()->index(cnt, 0);UNKNTableView->scrollTo(lastModelIndex); // 滚动到当前行cnt++;
}

5.用户类定义

用户类主要是指项目主窗口变量,定义了其构造函数、析构函数、UI界面信号槽函数、及相关类定义为私有变量,具体实现代码如下(示例):

#ifndef CSQLTEST_H
#define CSQLTEST_H#include <QMainWindow>
#include "testhread.h"
namespace Ui {
class CSqlTest;
}
class CSqlTest : public QMainWindow
{Q_OBJECT
public:explicit CSqlTest(QWidget *parent = nullptr);~CSqlTest();
private slots:void on_pBinsert_clicked();void on_pBchange_clicked();void on_pBlookup_clicked();void on_pBexit_clicked();void on_pBclea_clicked();void on_pBConditional_clicked();void on_pushButton_6_clicked();void on_pBstarthd_clicked();void on_pBpushmsg_clicked();
private:Ui::CSqlTest *ui;QSqlQueryModel  *m_pModel; //数据模型对象testhread*      DBthd;void displayRecord(QSqlQuery query); //展示query所查询到的数据
};
#endif // CSQLTEST_H

6.用户类使用

完成相关类私有变量初始化,主要包括Ui::CSqlTest *ui、QSqlQueryModel  *m_pModel、testhread *DBthd。还实现了UI界面上的信号槽功能,主要包括想表格中插入数据、更改表格、查询所有表格上所有数据、清除表等等,具体看UI界面,实现代码如下(示例):

#include "csqltest.h"
#include "ui_csqltest.h"
CSqlTest::CSqlTest(QWidget *parent): QMainWindow(parent), ui(new Ui::CSqlTest){ui->setupUi(this);/****创建线程类变量*****/DBthd = new testhread();/****将UI上表格视图地址赋值到线程变量上*****/DBthd->UNKNTableView = ui->UNTableView;/****表格初始化*****/DBthd->InitTableView();
}
CSqlTest::~CSqlTest()
{if(DBthd->isRunning()) {DBthd->terminate();DBthd->wait(1);}if(DBthd != nullptr)delete DBthd;delete ui;
}
void CSqlTest::on_pBstarthd_clicked()
{if(ui->pBstarthd->text() == "启动线程") {DBthd->g_flag = true;DBthd->start();ui->pBstarthd->setText("停止线程");}else {DBthd->g_flag = 0;ui->pBstarthd->setText("启动线程");}
}
//显示在表上QSqlQueryModel
void CSqlTest::on_pushButton_6_clicked()
{//为数据模型设置父对象m_pModel = new QSqlQueryModel(DBthd);//查询数据m_pModel->setQuery(QString("select * from student"));if(m_pModel->lastError().isValid()) {qDebug() << "Data query failed!!" << m_pModel->lastError().text();return;}//设置数据模型指定列的列标题(若是不设置,则标题为和数据表上一样)m_pModel->setHeaderData(0, Qt::Horizontal, "学号");m_pModel->setHeaderData(1, Qt::Horizontal, "姓名");m_pModel->setHeaderData(2, Qt::Horizontal, "年龄");m_pModel->setHeaderData(3, Qt::Horizontal, "身高");//将数据模型设置到tableView控件上ui->tableView->setModel(m_pModel);
}void CSqlTest::on_pBinsert_clicked()
{//插入表格QSqlQuery p;int id = ui->lineEdit_3->text().toInt();QString name = ui->lineEdit_2->text();int age = ui->lineEdit->text().toInt();int height = ui->lineEdit_4->text().toInt();QString tempinfo = QString("INSERT INTO student(id, name, age, height) VALUES(?,?,?,?)");p.prepare(tempinfo);p.addBindValue(id);p.addBindValue(name);p.addBindValue(age);p.addBindValue(height);if(!p.exec())qDebug() << "Fail to insert data. " << p.lastError().text();
}
void CSqlTest::displayRecord(QSqlQuery query)
{//获取一个记录对象QSqlRecord rec =  query.record();QString columTitle(""); //用于组列标题的变量for(int index = 0; index != rec.count(); ++index) {//循环获取每个列标题,并添加到列标题字符串中columTitle.append(rec.fieldName(index) + "\t");}//将列标题添加到显示控件中ui->textBrowser->append(columTitle);//循环获取每条记录while (query.next()){//将当前记录的数据组成字符串,然后添加到显示控件中QString record("");for(int index = 0; index != rec.count(); ++index) {record.append(query.value(index).toString() + "\t");}ui->textBrowser->append(record);}ui->textBrowser->append("================Run successfully==================\n");
}
void CSqlTest::on_pBlookup_clicked()
{QString tempinfo = "SELECT name FROM sqlite_master WHERE type='table';";if (!DBthd->sql->exec(tempinfo)) {qDebug() << "Error: Unable to execute query:" << DBthd->sql->lastError().text();}else{QStringList tableNames;while (DBthd->sql->next()) {QString name = DBthd->sql->value(0).toString();tableNames.append(name);}// 查询所有表上所有数据for (const QString &name : tableNames) {ui->textBrowser->append(QString("查询%1表所有数据:").arg(name));tempinfo = QString("SELECT * FROM %1").arg(name); // 从"student"这个表中查询出所有的数据, *表示查询表中的所有列if(!DBthd->sql->exec(tempinfo))qDebug() << "Error: Fail to query table. " << DBthd->sql->lastError();elsedisplayRecord(*(DBthd->sql));}}//查询完成后结束查询 清理和释放资源//DBthd->sql->finish();
}
void CSqlTest::on_pBConditional_clicked()
{QSqlQuery p;//添加准备执行的SQL语句(条件查询)p.prepare("select * from student where age > :age and id like ? ");p.bindValue(":age", 23);    //指定标识符赋值p.bindValue(1, "003");      //根据标识符下标赋值(从0开始)p.exec();   //直接使用空的exec运行ui->textBrowser->append("条件查询:");displayRecord(p);p.finish();//更新操作bool flag = p.exec("update student set age = 30 where id like '003'");if(!flag)return;ui->textBrowser->append("更新成功!!!!!!!!!!");
}
void CSqlTest::on_pBexit_clicked()
{DBthd->m_db.close();close();
}
void CSqlTest::on_pBclea_clicked()
{// 获取数据库中的所有表格名称QStringList tables = DBthd->m_db.tables();foreach (const QString& table, tables) {qDebug() << table;}if(tables.size() <= 1){return;}//删除数据表QSqlQuery p;for(int i=0;i<(tables.size()-1);i++){p.exec(QString("DROP TABLE %1").arg(tables.at(i)));}if(p.exec())qDebug() << p.lastError();elseqDebug() << "deleted table success";
}
void CSqlTest::on_pBchange_clicked()
{//在表格上更改student信息QString tempinfo = QString("UPDATE student SET name=:name,age=:age,height=:height WHERE id=:id");int id = ui->lineEdit_3->text().toInt();QString name = ui->lineEdit_2->text();int age = ui->lineEdit->text().toInt();int height = ui->lineEdit_4->text().toInt();DBthd->sql->prepare(tempinfo);DBthd->sql->bindValue(":id",id);DBthd->sql->bindValue(":name",name);DBthd->sql->bindValue(":age",age);DBthd->sql->bindValue(":height",height);if(!DBthd->sql->exec())qDebug() << DBthd->sql->lastError();elseqDebug() << "updated data success!";
}
void CSqlTest::on_pBpushmsg_clicked()
{if(ui->pBpushmsg->text() == "压力测试") {DBthd->pushmsg_flag = true;ui->pBpushmsg->setText("停止测试");}else {DBthd->pushmsg_flag = 0;ui->pBpushmsg->setText("压力测试");}
}

7.效果演示

完成上面代码后,编译运行,效果如下。

8.SQLite数据库

软件运行完后,在保存的目录路径下面,找到数据库文件,如下。其中dbmsg16-32-19.db文件在运行时按过按钮“清除旧表”,导致里面的内容被清空。

重新运行后,打开dbmsg16-36-58.db文件内容如下,可以看到保存的数据内容。

注意事项:要打开.db文件,需下载安装SQLiteStudio,博主使用的是SQLiteStudio-3.2.1版本的,相关链接见文末,里面已经下载好了的,直接安装使用即可。


总结

博文中相应的工程代码Qt-Case.zip 利用Qt开发软件进行编的例程,为博文提供案例-CSDN文库。


http://www.ppmy.cn/server/154163.html

相关文章

如何在 Spring Boot 微服务中设置和管理多个数据库

在现代微服务架构中&#xff0c;通常需要与多个数据库交互的服务。这可能是由于各种原因&#xff0c;例如遗留系统集成、不同类型的数据存储需求&#xff0c;或者仅仅是为了优化性能。Spring Boot 具有灵活的配置和强大的数据访问库&#xff0c;可以轻松配置多个数据库。在本综…

Linux top指令

top指令概述 top 是 Linux 系统中用于实时监控系统性能和进程信息的命令&#xff0c;功能强大且灵活。它提供了系统资源的动态视图&#xff0c;包括 CPU、内存、运行中的进程等。 这个指令可以说是Linux中最基本的工具了&#xff0c;用来监视系统的实时运行状态&#xff0c;类…

算法

探索算法世界&#xff1a;从基础到前沿 一、引言 算法是计算机科学的核心&#xff0c;它为解决各种问题提供了明确的步骤和方法。无论是数据处理、人工智能还是日常软件应用&#xff0c;算法都起着关键作用。 二、基础算法 排序算法 排序算法是最常见的算法之一。例如冒泡排序&…

Rtsplive-视频流-Linux部署

系统环境&#xff1a;Ubuntu-24.04-server JDK环境&#xff1a;≥java17 一、部署rtsplive 上传rtsplive-ubuntu-x64.tar.gz至linux-Ubuntu-24.04服务器 并解压至/opt目录下 二、安装JDK 使用Java 命令&#xff0c;检测是否有安装java&#xff0c;是否大于17版本 如系统安…

遗传算法——附python实现与各方法的作用与修改

前言 遗传算法是数学建模中非常重要的一种搜索和优化算法&#xff0c;掌握遗传算法的精髓除了在竞赛中具有优势以外&#xff0c;更主要的是在解决实际问题的时候提供了一种全新的思路&#xff0c;通过将现实中的某种模式转换成算法&#xff0c;并用以解决某种问题的这种思路&a…

info There appears to be trouble with your network connection. Retrying

这个错误信息表明你在使用包管理器安装项目依赖时遇到了网络连接问题。 可能的解决方法&#xff1a; 检查当前node.js版本是否过低。 建议使用当前长期支持版本 yarn的淘宝镜像&#xff1a;yarn的淘宝镜像-CSDN博客 nvm常用命令:NVM常用命令-CSDN博客 下载 | Node.js 中文…

网络安全的学习路线

最近在绿盟公司实习&#xff0c;看了这篇博客&#xff0c;点击这里 &#xff0c;有所感悟&#xff0c;自己记录一下网络安全的学习路线。 网络安全主要分别以下几种&#xff1a;1 web安全 2 系统安全 3二进制逆向 4 红蓝对抗 5 密码学 6 AI安全 7 移动&#xff08;ios,Anr…

Python 输出华丽分割线的方式大全

在 Python 编程中&#xff0c;有时候我们需要为日志、输出结果或者调试信息添加一些分割线&#xff0c;方便视觉上的区分。这篇文章将介绍几种常见的方法和一些创意的分割线设计&#xff0c;帮助你让代码输出更具美感和个性化。 方法 1: 基础分割线 使用 print() 和简单的字符…