环境:
Qt 版本:6.3.2
MySQL 版本:9.0
我已成功安装 MySQL 驱动程序,并且我的应用程序在单线程上下文中连接到数据库没有出现问题。
应用结构:
在我的应用程序中:
主线程从MySQL数据库读取数据并更新UI。
一个子线程从设备读取数据并将其存储在MySQL数据库中。
这是我在代码中添加数据库的方法:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "q");
虽然编译没有问题,但在尝试执行查询时出现运行时错误:
QSqlQuery::exec: database not open Query failed: QSqlError("", "Driver not loaded", "Driver not loaded")
或者,如果我使用这个更简单的声明:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
没有运行时异常,但我遇到了与线程所有权相关的错误:
QSqlDatabasePrivate::database:
请求的数据库不属于调用线程。
问题:
这两种QSqlDatabase声明方法有什么区别?
如何在Qt中正确管理跨线程的MySQL数据库连接?
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
threadA= new Cdatathread(this);
connect(threadA,&Cdatathread::started, this, &MainWindow::do_threadA_started);
connect(threadA,&Cdatathread::finished,this, &MainWindow::do_threadA_finished);
connect(threadA,&Cdatathread::newValue,this, &MainWindow::do_threadA_update);
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("rice");
db.setUserName("root");
db.setPassword("root123");
bool ok = db.open();
if (ok){
QMessageBox::information(this, "infor", "success");
}
else {
QMessageBox::information(this, "infor", "open failed");
}
}
:
void MainWindow::do_threadA_update()
{
QSqlQuery query;
if (query.exec("SELECT hexvalue FROM value WHERE address='D100' ORDER BY timestamp_column DESC LIMIT 1")) {
if (query.next()) {
latestValueOfA = query.value(0).toString();
qDebug()
:
void Cdatathread::run() {
m_stop = false;
m_paused = true;
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("rice");
db.setUserName("root");
db.setPassword("root123");
db.open();
while (!m_stop) //循环主体
{
if (!m_paused) {
qDebug() << "testing";
QSqlQuery query;
query.exec("SELECT * FROM setting");
while (query.next()) {
QString ip = query.value(1).toString();
int port = query.value(2).toInt();
QString address = query.value(3).toString();
QTcpSocket socket;
socket.connectToHost(ip, port);
if (socket.waitForConnected()) {
QByteArray data = sendtxt(query.value(3).toString(), query.value(4).toString());
socket.write(data);
if (socket.waitForReadyRead()) {
QByteArray rec_data = socket.readAll();
QString hexData = rec_data.toHex().toUpper();
qDebug() << hexData;
QSqlQuery query;
query.prepare("INSERT INTO value (ip, address, hexValue, datetime) VALUES (:ip, :address, :hexValue)");
query.bindValue(":ip", ip);
query.bindValue(":address", port);
query.bindValue(":hexValue", hexData);
if (!query.exec()) {
qDebug() << "Error inserting data into value table";
} else {
qDebug() << "Data inserted successfully into value table";
}
}
}
}
}
}
emit newValue();
}
msleep(500);
}
quit();
}
void Cdatathread::run() {
m_stop = false;
m_paused = true;
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("rice");
db.setUserName("root");
db.setPassword("root123");
db.open();
while (!m_stop) //循环主体
{
if (!m_paused) {
qDebug() << "testing";
QSqlQuery query;
query.exec("SELECT * FROM setting");
while (query.next()) {
QString ip = query.value(1).toString();
int port = query.value(2).toInt();
QString address = query.value(3).toString();
QTcpSocket socket;
socket.connectToHost(ip, port);
if (socket.waitForConnected()) {
QByteArray data = sendtxt(query.value(3).toString(), query.value(4).toString());
socket.write(data);
if (socket.waitForReadyRead()) {
QByteArray rec_data = socket.readAll();
QString hexData = rec_data.toHex().toUpper();
qDebug() << hexData;
QSqlQuery query;
query.prepare("INSERT INTO value (ip, address, hexValue, datetime) VALUES (:ip, :address, :hexValue)");
query.bindValue(":ip", ip);
query.bindValue(":address", port);
query.bindValue(":hexValue", hexData);
if (!query.exec()) {
qDebug() << "Error inserting data into value table";
} else {
qDebug() << "Data inserted successfully into value table";
}
}
}
}
}
}
emit newValue();
}
msleep(500);
}
quit();
}
:
如何在Qt中正确管理跨线程的MySQL数据库连接?
根据Qt自己的说法
数据库连接只能在创建它的线程内使用。可以使用 QSqlDatabase::moveToThread() 将连接移动到另一个线程中。
此外,QSqlDrivers 使用的第三方库可以对在多线程程序中使用 SQL 模块施加进一步的限制。有关更多信息,请参阅数据库客户端的手册
https://doc.qt.io/qt-6/threads-modules.html#threads-and-the-sql-module
这两种QSqlDatabase声明方法有什么区别?
两者之一接受connectionName作为参数
使用驱动程序类型和连接名称connectionName 将数据库添加到数据库连接列表中。如果已存在名为 connectionName 的数据库连接,则该连接将被删除。
数据库连接由connectionName 引用。返回新添加的数据库连接。
如果类型不可用或无法加载,isValid() 返回 false。
如果未指定connectionName,新连接将成为应用程序的默认连接,并且后续调用不带连接名称参数的database()将返回默认连接。如果此处提供了连接名称,请使用数据库(连接名称)检索连接。