如何在多线程环境下处理Qt + MySQL?

问题描述 投票:0回答:1

环境:

  • 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:
请求的数据库不属于调用线程。

问题:

  1. 这两种QSqlDatabase声明方法有什么区别?

  2. 如何在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();
}
multithreading qt mysql-connector
1个回答
0
投票

如何在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()将返回默认连接。如果此处提供了连接名称,请使用数据库(连接名称)检索连接。

https://doc.qt.io/qt-6/qsqldatabase.html#addDatabase

© www.soinside.com 2019 - 2024. All rights reserved.