• QT--多线程与MySQL数据库


    本文为学习记录,若有错误,请联系作者,谦虚受教


    前言

    既然错过了星星 就别再错过月亮.


    多线程和数据库的程序会用到下面相关知识。

    一、多线程

    1.进程和线程

    进程是:电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的
    例如:微信和浏览器是相互独立存在的
    在这里插入图片描述
    而线程是进程的最小单位,也就是说一个进程里至少有一个线程。
    对于多线程的理解,附上图片(源自于B站教程)
    (图①)

    2.线程同步

    线程同步并不是同时进行,是而互相配合、协同,按照先后次序进行运行。你先做完,我再做。
    线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!

    根据图一,可以看出来,每个线程工作并不是同时进行的,而是抢CPU的时间片,谁先抢到了就谁先执行。针对使用公共资源的时候,采用线程互斥量来保证线程同步。

    3.线程互斥

    线程互斥(Mutex)是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有很多个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。也是代码中的上锁(lock)和解锁(unlock)。

    互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

    二、MYSQL

    1.mysql驱动

    连接mysql的时候出现的错误

    QSqlDatabase: QMYSQL driver not loaded
    数据库:未加载QMYSQL驱动程序
    
    • 1
    • 2

    所以,在程序运行前保证自己QT的安装目录下有mysql驱动。

    在这里插入图片描述
    这里附上百度网盘MYSQL的驱动(32位和64位)
    https://pan.baidu.com/s/1WGZBfGD0K_XTZgmDnQ6ORg\n\n密码:qwer

    重点!!!!

    当前线程创建数据库对象和查询对象只能在当前线程中使用,不能跨线程使用

    这里说的是一个线程创建的 QSqlDatabase 对象和 查出来的 QSqlQuery 对象只能在当前线程中使用。

    一个数据库的连接名称是可以在不同线程中使用的!

    一般默认连接名称是 “qt_sql_default_connection”,这里我们设置成“MySql”
    g_db = QSqlDatabase::addDatabase(“QMYSQL”,“MySql”);//连接驱动 数据库连接名

    在多线程和数据库的运用中,如果多个线程操控同一个数据库的话,我们可以建立一个数据库连接名,主线程和子线程共用一个连接。主线程创建连接,然后可以去操作(查询等)。而子线程获得主线程创建的连接名称,进而去操作(查询等)。
    我们利用这点在主线程中打开数据库,子线程中使用数据库。

    三、主线程打开数据库

    //数据库设置
        if(QSqlDatabase::contains("qt_sql_default_connection"))
        {
            g_db = QSqlDatabase::database("qt_sql_default_connection");
            ui->label_Setting->setText("qt_sql_default_connection");
        }
        else
        {
            g_db = QSqlDatabase::addDatabase("QMYSQL","MySql");//连接驱动 数据库连接名
            ui->label_Setting->setText("QSQLITE");
        }
        if(g_db.isValid())
        {
            ui->label_Setting->setText("Valid");
        }
        else
        {
             ui->label_Setting->setText("NO Valid");
        }
        
        g_db.setHostName(strHost);           //主机名
        g_db.setPort(iPort);                 //端口名
        g_db.setDatabaseName("test1");       //数据库名称 (默认设置)
        g_db.setUserName(strUserName);       //用户名     (root)
        g_db.setPassword(strPasse);          //密码       (root)
        g_db.open();
    
        // 创建数据库,把下面的newDataBase替换成你要创建的数据库名字。
        QString newDataBase = QString("%1%2").arg("create database if not exists").arg(ui->edit_DBName->text());
        g_db.exec(newDataBase);
        // 连接上刚刚创建好的数据库,重新设置数据库名称
        g_db.setDatabaseName(ui->edit_DBName->text());
        // 必须重新调用一遍.open(),这里不加这一句,后面就会报错"QSqlQuery::exec: database not open"
        if(!g_db.open())
        {
            QSqlError  slqErr = g_db.lastError();
            qDebug()<<"数据库连接失败"<<slqErr;
            ui->lb_connState->setText("连接数据库失败");
            ui->lb_connState->setStyleSheet("color:red;");
            return false;
        }
        else
        {
            qDebug() << "当前打开数据库线程id:" << QThread::currentThread();
            ui->lb_connState->setText("连接成功");
            m_cboard.StartRun(Selete_JustTimeOpenDB);   
            ui->lb_connState->setStyleSheet("color:green;");
            EnableControl(false);
            return true;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述

    四、子线程使用数据库

    1.子线程创建数据表

    if(!g_db.open())
        {
            emit sig_SetTimeONState("数据库未打开",false);
        }
        else
        {
            qDebug() << "当前添加数据表线程id:" << QThread::currentThread();
            for(int i =0;i< NUM_SENSOR ; i++)  //NUM_BOARD
            {
                QString newSheetName = QString("%1%2").arg("Board").arg(i+1);
                QString CreateTable = QString("%1%2%3").arg("create table if not exists ").arg(newSheetName).arg("(Board_id int,Sensor_id int,DL float,DY float,NZ float,GL float,Time varchar(30))");
                g_db.exec(CreateTable);
    
                QSqlError  slqErr = g_db.lastError();
                qDebug()<<"数据表创建失败"<<slqErr;
            }
            QApplication::processEvents();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    2.子线程插入数据

    if(g_db.isOpen() == true)
        {
            QSqlQuery query = QSqlQuery(g_db);    // !!重点
            sheetName = QString("%1%2%3%4").arg("insert into ").arg("Board").arg((m_iBoardID+1)).arg("(Board_id,Sensor_id,DL,DY,NZ,GL,Time) values('%1','%2','%3','%4','%5','%6','%7')")
                        .arg(m_iBoardID+1).arg(i+1).arg(QString::number((double)ALLdata1[i],'f',2)).arg(QString::number((double)ALLdata2[i],'f',2))
                        .arg(QString::number((double)ALLdata3[i],'f',2)).arg(QString::number((double)ALLdata4[i],'f',2))
                        .arg(strdatatime);
                qDebug()<<"插入数据库信息"<<sheetName;
                query.prepare(sheetName);
                if(query.exec())
                {
                    qDebug()<<"插入数据到MYSQL成功!";
                }
                else
                {
                    QSqlError lastError=query.lastError ();
                    qDebug()<<lastError.driverText ()<<QString(QObject::tr ("失败\n"));
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    注意,连接数据库有多个连接名时,创建查询对象的时候要指定哪个数据库连接名,否则会是默认的数据库连接名。
    QSqlQuery query = QSqlQuery(g_db);

    2.子线程导出数据表

        g_Mutex.lock();
        qDebug() << "数据库导出表格线程id:" << QThread::currentThread();
    
        if(!g_db.open())
        {
            emit sig_SetTimeONState("数据库未打开",false);
        }
        else
        {
            emit sig_SetTimeONState("数据库已打开",true);
        }
    
        QSqlQuery query = QSqlQuery(g_db);
        QXlsx::Document xlsx;
        QXlsx::Format title_format; /*设置标题的样式*/
        QXlsx::Format format2;/*小标题样式*/
        QXlsx::Format format3;/*数据内容样式*/
        //增加sheet NUM_BOARD
        for(int j=0;j< 32;j++)
        {
            //建立Sheet
            QString QStrSheetName;
            QStrSheetName = "Light";
            QStrSheetName += QString::number(j+1);//j+1
            xlsx.addSheet(QStrSheetName);
    
            title_format.setBorderStyle(QXlsx::Format::BorderThin);//外边框
            format2.setBorderStyle(QXlsx::Format::BorderThin);//外边框
            format3.setBorderStyle(QXlsx::Format::BorderThin);//外边框
            xlsx.setRowHeight(1,1,25);/*设置标题行高*/
            xlsx.setColumnWidth(1,7,20);/*设置列宽,一共5列参数*/
    
            title_format.setFontSize(11);
            title_format.setFontColor(QColor(Qt::red));
            title_format.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
            title_format.setVerticalAlignment(QXlsx::Format::AlignVCenter);
            xlsx.mergeCells("A1:G1",title_format);//合并1~6列写入标题
            xlsx.write("A1","TestData  Information");
    
            format2.setFontColor(QColor(Qt::blue));
            format2.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
            xlsx.write("A2", "BoardID", format2);/*写入文字,应该刚才设置的样式*/
            xlsx.write("B2", "SensorID", format2);
            xlsx.write("C2", "电流", format2);
            xlsx.write("D2", "电压", format2);
            xlsx.write("E2", "内阻", format2);
            xlsx.write("F2", "功率", format2);
            xlsx.write("G2", "时间", format2);
    
            format3.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
            QString selecMYSQL  = QString("%1%2%3%4%5%6%7%8").arg("select * from ").arg("Board").arg(m_iBoardID+1).arg(" where Sensor_id ").arg("in('").arg(j+1).arg("')").arg(";");
            qDebug()<<"导出数据库语句信息:"<<selecMYSQL;
            query.exec(selecMYSQL);
            QSqlError lastError=query.lastError ();
            qDebug()<<QString(QObject::tr ("错误\n"))<<lastError.driverText ();
            int i=3;
            while(query.next())//一行一行遍历
            {
                xlsx.write(i,1,query.value(0).toInt(),format3);
                xlsx.write(i,2,query.value(1).toInt(),format3);
                xlsx.write(i,3,query.value(2).toFloat(),format3);
                xlsx.write(i,4,query.value(3).toFloat(),format3);
                xlsx.write(i,5,query.value(4).toFloat(),format3);
                xlsx.write(i,6,query.value(5).toFloat(),format3);
                xlsx.write(i,7,query.value(6).toString(),format3);
                i++;
            }
        }
        //设置excel表格的默认文件名为"Boardi + 测试数据+ 当前时间"
        QString current_date =QDateTime::currentDateTime().toString(Qt::ISODate);
        QDateTime datatime=QDateTime::currentDateTime();
        QString strdatatime;
        strdatatime=datatime.date().toString("yyyy-MM-dd ")+datatime.time().toString("hh.mm.ss");
        QString fileName=QString("%1%2%3%4").arg("Board").arg(m_iBoardID+1).arg("测试数据-").arg(strdatatime);
        QString dir=QString("/%1").arg(fileName);
        QString dir1=dir.replace(QRegExp(":"),"-");
    
        //2.获取应用程序可执行文件的文件.exe路径:
        QString path=QCoreApplication::applicationDirPath();/*保存文件的路径*/
        QString excelsavepath = QString("%1%2").arg(path).arg(dir1);
    
        QString dtStr=path+dir1+".xlsx";
        qDebug()<<"保存路径"<<dtStr;
        xlsx.saveAs(dtStr);/*保存*/
        sleep(2);
        g_Mutex.unlock();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    如果是多个子线程操控数据库的时候,记得加锁,防止造成资源争夺。
    在这里插入图片描述

    五、MYSQL中的查询

    – 下面是对我数据库中的学生表stuinfo进行的查询操作

    – 1.查询学生信息表中的所有学生的姓名与地址
    SELECT stuName AS ‘姓名’ , stuAddr AS ‘地址’ FROM stuinfo;

    – 2.将多个列的结果合并为一个列
    SELECT CONCAT(stuName, ‘-’, stuAddr) AS ‘姓名-地址’ FROM stuinfo;

    – 3.过滤重复的学生姓名
    SELECT DISTINCT stuName FROM stuinfo;

    – 4.限制数据查询的数量
    SELECT * FROM stuinfo LIMIT 3,6;

    – 5.按照学生序号查询序号最大的三个人
    SELECT * FROM stuinfo ORDER BY stuNO DESC LIMIT 0, 3;

    – 6.查询所有男生
    SELECT * FROM stuinfo WHERE stuSex = ‘男’;

    – 7.查询所有编号大于10010的学生
    SELECT * FROM stuinfo WHERE stuNO > 10010;

    – 8.查询所有不是云南的的学生
    SELECT * FROM stuinfo WHERE stuAddr != ‘云南’;

    – 9.查询编号在10005到10015之间的学生
    SELECT * FROM stuinfo WHERE stuNO >= 10005 AND stuNO <= 10015;

    – 10.between and 区间查询
    SELECT * FROM stuinfo WHERE stuNO BETWEEN 10005 AND 10015;

    – 11.查询数据库中张三,李四, 王五的信息
    SELECT * FROM stuinfo WHERE stuName IN(‘张三’, ‘李四’, ‘王五’);

    – 12.查询数据库中姓名不是张三,李四,王五的信息
    SELECT * FROM stuinfo WHERE stuName NOT IN(‘张三’, ‘李四’, ‘王五’);

    – 13.查询学生中姓王的学生
    SELECT * FROM stuinfo WHERE stuName LIKE ‘王_%’;

    – 14.查询姓王,编号大于10005的信息
    SELECT * FROM stuinfo WHERE stuName LIKE ‘王_%’ AND stuNO >= 10005;

    – 15.查询爱好为空的数据 NULL不能使用任何运算符进行运算
    – 只能使用IS来判定
    SELECT * FROM stuinfo WHERE stuFav IS NULL;
    SELECT * FROM stuinfo WHERE stuFav IS NOT NULL;

    – 16.统计所有学生一共加起来的分数
    SELECT SUM(stuScore) AS ‘TotalScore’ FROM stuinfo;

    – COUNT 函数用于计数(用姓名统计时 把同名的学生忽略)
    – 17.统计学生表中一共有多少学生
    SELECT COUNT(stuNO) FROM stuinfo;

    – MAX用于求列中的最小值 MIN相反
    – 18.求分数最高的学生是多少分
    SELECT MAX(stuScore) FROM stuinfo;

    – 19.求学生表中学生编号的最大差异
    – SELECT (SELECT MAX(stuNO) FROM stuinfo) - (SELECT MIN(stuNO) FROM stuinfo)
    SELECT (MAX(stuNO) - MIN(stuNO)) FROM stuinfo;

    – AVG 函数用于求取平均数
    – 20.查询学生的平均分
    SELECT AVG(stuScore) FROM stuinfo;

    – 21.求大于平均分数的学生人数
    SELECT COUNT(stuNO) FROM stuinfo
    WHERE stuScore > (SELECT AVG(stuScore) FROM stuinfo);

    – 22.按地域分组查看每个省有多少个学生
    SELECT stuAddr,COUNT(stuNO) FROM stuinfo
    GROUP BY stuAddr;

    – 23.按地域分组查看每个省学生的平均成绩
    SELECT stuAddr,AVG(stuScore) FROM stuinfo
    GROUP BY stuAddr;

    – 24.按地域分组查看每个省成绩大于60分的学生数量
    SELECT stuAddr, COUNT(stuNO) FROM stuinfo
    GROUP BY stuAddr, stuScore
    HAVING stuScore > 60;

    – 多表连接查询 常用三种 内连接(交集) 左连接() 右连接
    – 25.查询所有学生的信息
    SELECT * FROM stuinfo, stuclass; – 无条件的链接会做笛卡尔集
    SELECT * FROM stuinfo, stuclass WHERE stuinfo.classID = stuclass.classID;
    SELECT * FROM stuinfo INNER JOIN stuclass ON stuinfo.classID = stuclass.classID;

    – 26.左连接 优先顾全左表数据 如果左表的数据在右表中没有对应,则用NULL显示右表的列
    SELECT * FROM stuinfo LEFT JOIN stuclass ON stuinfo.classID = stuclass.classID;

    – 27.右连接
    SELECT * FROM stuinfo RIGHT JOIN stuclass ON stuinfo.classID = stuclass.classID;

    – 28.查询一年级所有学生的信息
    SELECT * FROM stuinfo INNER JOIN stuclass ON stuinfo.classID = stuclass.classID
    WHERE className LIKE ‘一年级%’;

    总结

    善于总结,多进一步。

  • 相关阅读:
    基于dq0变换的三相并联有源电力滤波器模拟模型(Simulink)
    2022/08/08 day05:Jedis
    Spring Boot - filter 的顺序
    功能测试如何编写测试用例
    CentOS系统环境搭建(十九)——CentOS7安装chat GPT
    React81_React.memo
    IDEA中SpringBoot的启动类文件变成了一个J文件的解决方案
    【数据结构】栈的知识点总结--关于栈的定义和基本操作;C语言实现栈;顺序栈;链栈;共享栈;栈的易错点的总结
    如何将pdf拆分为单页?推荐这些方法
    分布式ID选型对比(2)
  • 原文地址:https://blog.csdn.net/m0_51988927/article/details/127573543