QT--多线程与MySQL数据库

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


前言

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


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

一、多线程

1.进程和线程

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

2.线程同步

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

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

3.线程互斥

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

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

二、MYSQL

1.mysql驱动

连接mysql的时候出现的错误

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

所以,在程序运行前保证自己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.子线程创建数据表

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();
    }

在这里插入图片描述

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"));
            }
        }

在这里插入图片描述

注意,连接数据库有多个连接名时,创建查询对象的时候要指定哪个数据库连接名,否则会是默认的数据库连接名。
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();

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

五、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 ‘一年级%’;

总结

善于总结,多进一步。

猜你喜欢

转载自blog.csdn.net/m0_51988927/article/details/127573543