Qt之数据库使用(十四)

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


目录

前言

一、SQLite

二、SQLite使用

1.添加SQL

2.创建数据库

3.定义类及相关变量

4.相关功能函数

5.用户类定义

6.用户类使用

7.效果演示

8.SQLite数据库

总结


前言

Qt支持的数据库包括SQLiteMySQLPostgreSQLODBC等‌。其中,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模块,如下所示。

c 复制代码
QT       += sql

2.创建数据库

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

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

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

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

c 复制代码
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;
    } else
        qInfo() << "Successfully opened database! SQLite Type.";
    //**5**开始事务
    if (!m_db.transaction()) {
        qDebug() << "Failed to begin transaction";
        return;
    }
    //**6**创建数据库QSqlQuery
    sql = new QSqlQuery(m_db);
    //**7**在数据库上创建表格-student
    QString 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();
    else
        qDebug() << "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();
    else
        qDebug() << "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();
            else
                displayRecord(*(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();
    else
        qDebug() << "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();
    else
        qDebug() << "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文库

相关推荐
百锦再4 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip
tryCbest4 天前
数据库SQL学习
数据库·sql
cowboy2584 天前
mysql5.7及以下版本查询所有后代值(包括本身)
数据库·sql
努力的lpp4 天前
SQL 报错注入
数据库·sql·web安全·网络安全·sql注入
麦聪聊数据4 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
山峰哥4 天前
吃透 SQL 优化:告别慢查询,解锁数据库高性能
服务器·数据库·sql·oracle·性能优化·编辑器
范特西.i4 天前
QT聊天项目(8)
开发语言·qt
枫叶丹44 天前
【Qt开发】Qt界面优化(七)-> Qt样式表(QSS) 样式属性
c语言·开发语言·c++·qt
轩情吖4 天前
MySQL初识
android·数据库·sql·mysql·adb·存储引擎