本文为学习记录,若有错误,请联系作者,谦虚受教
既然错过了星星 就别再错过月亮.
多线程和数据库的程序会用到下面相关知识。
进程是:电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的
例如:微信和浏览器是相互独立存在的
而线程是进程的最小单位,也就是说一个进程里至少有一个线程。
对于多线程的理解,附上图片(源自于B站教程)
(图①)
线程同步并不是同时进行,是而互相配合、协同,按照先后次序进行运行。你先做完,我再做。
线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!
根据图一,可以看出来,每个线程工作并不是同时进行的,而是抢CPU的时间片,谁先抢到了就谁先执行。针对使用公共资源的时候,采用线程互斥量来保证线程同步。
线程互斥(Mutex)是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有很多个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。也是代码中的上锁(lock)和解锁(unlock)。
互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。
连接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;
}
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();
}
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);
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();
如果是多个子线程操控数据库的时候,记得加锁,防止造成资源争夺。
– 下面是对我数据库中的学生表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 ‘一年级%’;
善于总结,多进一步。