前言支持內置數據庫:
驅動關系:
拿Mysql舉例,我們的Qt程序有自己的驅動,libqsqlmysql.dll,如果Qt安裝好了之后沒有這個動態庫,則需要自己使用qmake編譯。如果有了這個驅動,說明我們的Qt環境已經ok了,但是如果需要訪問Mysql數據庫,還需要Mysql提供的訪問它的驅動libmysql.dll【linux對應libmysqlclient.so.18,不同mysql版本名字不一樣】。總結就是:Qt程序->libqsqlmysql.dll->libmysql.dll->Mysql數據庫
libqsqlmysql.dll:安裝Qt附帶或者源代碼編譯
libmysql.dll:安裝mysql或者直接網上找這個動態庫
一、sqlite
1、在頭文件中聲明數據庫對象
QSqlDatabase db;
2、在構造函數中定義對象(最好這樣定義,因為對於db來說只需要addDatabase一次,否則多次addDatabase會報錯)
if(QSqlDatabase::contains("qt_sql_default_connection"))
db = QSqlDatabase::database("qt_sql_default_connection");
else
db = QSqlDatabase::addDatabase("QSQLITE");
3、設置數據庫文件路徑
db.setDatabaseName(".//qtDb.db");//設置數據庫文件名字,選擇的是當前路徑
4、打開數據庫
if(!db.open())
{
qDebug() << "打開數據庫失敗";
return;
}
5、判斷數據庫中是否存在某表,不存在則新建(數據庫指令相關)
QSqlQuery query(db);
bool isTableExist = query.exec("select * from CSSBDB");//關鍵的判斷
if(!isTableExist)//表不存在,新建表
{
bool success = query.exec("create table CSSBDB(id INTEGER PRIMARY KEY,設備名稱 varchar,設備型號 varchar,固定資產編號 varchar,測試部自編號 varchar,卡片編號 varchar,數量 varchar,單價 varchar,啟用時間 varchar,用途 varchar,使用人或保管人 varchar,備注 varchar)");//創建數據表
if(success)
{
qDebug() <<"數據庫表1創建成功!";
}
else
{
qDebug() <<"數據庫表1創建失敗!";
QSqlError tempErr = query.lastError();
qDebug()<<tempErr;
return;
}
}
6、獲取數據庫中有多少數據(行數和列數)
query.exec("select * from CSSBDB");
QSqlQueryModel *model = new QSqlQueryModel();
model->setQuery(query);
int nRecordCount = model->rowCount();//行數
int nColumnCount = model->columnCount();//列數
7、獲取表的表頭內容
if(query.exec("PRAGMA table_info(CSSBDB)"))
{
QStringList tempList;
while(query.next())
{
tempList.append(query.value(1).toString());
}
}
下圖是獲取表信息結果
8、更新某條記錄
update CSSBDB set companyNumber='050689' where id=2
9、插入一條記錄
insert into CSSBDB values(null,"台式主機","DELL9020MT","050688","CSB-PC001-A","123","1","3000","2016.4.15","服務器","","")//如果id是null則表示id按順序增加1
10、關閉數據庫
db.close();
二、Access
{ QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "EXPORT_EXCEL"); QString dsn = QString("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=%1").arg(filePath);// DB_PATH db.setDatabaseName(dsn);//設置數據庫路徑 QString sqlStr; if (!db.open()) { qDebug() << "打開數據庫失敗"; } else { QSqlQuery query(db); sqlStr = ""; sqlStr = QString("SELECT * FROM tableName"); query.exec(sqlStr); }
} QSqlDatabase::removeDatabase("EXPORT_EXCEL");
ps:
1、query.next(數據庫命令)是每次返回一行數據,要取出當前行的某列數據,使用query.value(n),n從0開始
2、判斷當前qt可用的數據庫驅動
QStringList drivers = QSqlDatabase::drivers();
foreach(QString driver, drivers)
qDebug() << "\t" << driver;
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "test");
qDebug() << "ODBC driver valid?" << db.isValid();
3、query.exec,如果sql語句沒錯,拿它是不會報錯的,即時返回值為空,它也沒錯
4、數據庫回收
{
//這里用{}畫出一個作用域,當這個域完成之后,數據庫的所有操作動作都被回收了
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");//數據庫,設置為Access2000
QString dsn = QString("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=%1").arg(DB_PATH);//這是access的數據庫,需要與當前電腦數據庫驅動一致:控制面板,數據源,ODBC
db.setDatabaseName(dsn);//設置數據庫路徑
db.open();
//數據庫操作
}
QSqlDatabase::removeDatabase("qt_sql_default_connection");//這句話之前保證當前連接的數據庫沒有任何數據操作,由上面的作用域實現
5、數據庫原理:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
上面這句話是新建一個連接,每個連接是對數據庫的唯一標識,如果像上面這樣寫,連接名稱就是默認的qt_sql_default_connection,最后回收的時候也是QSqlDatabase::removeDatabase("qt_sql_default_connection")
如果在程序中多個地方【線程】操作數據庫:QSqlDatabase db = QSqlDatabase::addDatabase("QODBC","MyConnction");
回收:QSqlDatabase::removeDatabase("MyConnction");
6、有時候出現問題:"[Microsoft][ODBC 驅動程序管理器] 未發現數據源名稱並且未指定默認驅動程序 QODBC3: Unable to connect"
1、dsn語句有誤
2、數據庫驅動不正確,當時我編譯的是64位的,正常運行,后來改成了32位的,報上面錯,下載32位的驅動安裝了就ok了
7、解決通過model->rowCount();只能返回最多256個數據
while(model->canFetchMore())
{
model->fetchMore();
}
8、使用了數據庫時,有時打包軟件出現“driver not load”
1)將C:\Qt\Qt5.3.1\5.3\msvc2012路徑下的文件夾plugins復制到exe文件目錄下,打開plugins,只保留sqldrivers文件夾,需要確認里面是否有你需要的驅動, 如:程序中使用了QSqlite數據庫,則需要有qsqlite.dll(發布版)qsqlited.dll(調試版),
2)在main.cpp文件中添加下面第二行和第三行:
QApplication a(argc, argv);
QString strLibPath(QDir::toNativeSeparators(QApplication::applicationDirPath())+QDir::separator()+"plugins");
a.addLibraryPath(strLibPath);
9、將數據庫快速轉Excel的辦法
sqlStr = "SELECT * INTO [excel 8.0;database=.\\Data\\export\\1.xls].Sheet1 FROM tableName";
三、mysql
3.1、自己封裝的單次訪問數據庫類,自動實現釋放連接
頭文件:
#ifndef MYSQLTOOL_H
#define MYSQLTOOL_H #include <QSqlDatabase> #include "includes.h" #include <QSqlQuery> #include <mutex> class Mysqltool { public: Mysqltool(QString dataBaseName); virtual ~Mysqltool(); public: bool exec(QString sql);//插入、刪除、更新 bool select(QString sql,QSqlQuery& rsltQuery);//查詢 private: QSqlDatabase* pDb; QString dataBaseName; void setRandString(QString & randString);//獲取隨機字符串 }; extern std::mutex m_mutext_opensql; #endif // MYSQLTOOL_H
源文件:
#include "mysqltool.h"
#include "mymethod.h" #include <QSqlError> #include <QTime> std::mutex m_mutext_opensql; /*****************************************************************/ //作者:朱小勇 //函數名稱:構造函數 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ Mysqltool::Mysqltool(QString dataBaseName) { QString randStr;setRandString(randStr); dataBaseName += randStr; #if CLOSE_IF Mymethod::record(dataBaseName); #endif pDb = new QSqlDatabase(QSqlDatabase::addDatabase("QMYSQL", dataBaseName)); pDb->setHostName(DB_IP); //ip pDb->setUserName(DB_USER_NAME); //登陸MYSQL的用戶名 pDb->setPassword(DB_PASSWORD); //登陸的密碼 pDb->setDatabaseName(DB_DATABASE_NAME); //數據庫的名稱 m_mutext_opensql.lock(); if(!pDb->open()) { Mymethod::record("數據庫打開失敗:"); // QSqlError tempErr = db.lastError(); // qDebug()<<tempErr; } m_mutext_opensql.unlock(); this->dataBaseName = dataBaseName; } /*****************************************************************/ //作者:朱小勇 //函數名稱:析構函數 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ Mysqltool::~Mysqltool() { pDb->close(); delete pDb; pDb = nullptr; QSqlDatabase::removeDatabase(dataBaseName); } /*****************************************************************/ //作者:朱小勇 //函數名稱:增刪改 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ bool Mysqltool::exec(QString sql) { bool ret = true; RET_VALUE_IF_EAQU(pDb->isOpen(),false,false); QSqlQuery query(*pDb); #if OPEN_IF if(!query.exec(sql)) { QSqlError tempErr = query.lastError(); qDebug()<<tempErr; qDebug()<<"sql:"<<sql; } #endif #if CLOSE_IF RET_VALUE_IF_EAQU(query.exec(sql),false,false); #endif return ret; } /*****************************************************************/ //作者:朱小勇 //函數名稱:查 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:如果有問題可以將查詢結果放在堆上 /*****************************************************************/ bool Mysqltool::select(QString sql,QSqlQuery& rsltQuery) { bool ret = true; QSqlQuery query(*pDb); RET_VALUE_IF_EAQU(query.exec(sql),false,false); rsltQuery = query; return ret; } /*****************************************************************/ //作者:朱小勇 //函數名稱:獲取隨機字符串,保證連接名不同 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ void Mysqltool::setRandString(QString & randString) { int max = 8; QString tmp = QString("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWZYZ"); QString str; QTime t; t= QTime::currentTime(); qsrand(t.msec()+t.second()*1000); for(int i=0;i<max;i++) { int ir = qrand()%tmp.length(); str[i] = tmp.at(ir); } randString = str; }
注意:在removeDatabase之前要保證數據庫相關的對象都回收了,所以將db設計成指針,如果db是類成員變量,那么removeDatabase的時候db永遠存在,就會失敗。
這是自己封裝的訪問數據庫類,工作原理是每次訪問數據庫都在對應的線程里建立一個連接,並建立db對象,再訪問。缺點是需要每次都建立數據庫,浪費時間和數據庫資源。
下面使用數據庫連接池的方式來封裝【參考網上的】
3.2、網上借鑒的數據庫連接池,問題其實都很多。釋放的時候鎖應該放最上面【原文是先方式互斥體再使用鎖,讓我弄了好久】
ConnectPool.h
#ifndef CONNECTPOOL_H #define CONNECTPOOL_H #include <QObject> #include <QtSql> #include <QQueue> #include <QMutex> #include <QMutexLocker> class ConnectPool : public QObject { Q_OBJECT public: static void release(); //關閉所有數據庫連接 static QSqlDatabase openConnection(); //獲取數據庫連接 static void closeConnection(QSqlDatabase connection); //釋放數據庫連接回連接池 ~ConnectPool(); public: static ConnectPool& getInstance(); private: ConnectPool(); ConnectPool(const ConnectPool &other)=default; ConnectPool& operator=(const ConnectPool &other)=default; QSqlDatabase createConnection(const QString &connectionName); //創建數據庫連接 QQueue<QString> usedConnectionNames; //已使用的數據庫連接名 QQueue<QString> unusedConnectionNames; //未使用的數據庫連接名 //數據庫信息 QString hostName; QString databaseName; QString username; QString password; QString databaseType; bool testOnBorrow; //取得連接的時間驗證連接是否有效 QString testOnBorrowSql; //測試訪問數據庫的SQL int maxWaitTime; //最大等待時間 int waitInterval; //嘗試獲取連接時等待間隔時間 int maxConnectionCount; //最大連接數 static QMutex mutex; static QWaitCondition waitConnection; static ConnectPool *instance; signals: public slots: }; #endif // CONNECTPOOL_H
ConnectPool.cpp
#include "connectpool.h" QMutex ConnectPool::mutex; QWaitCondition ConnectPool::waitConnection; ConnectPool* ConnectPool::instance = nullptr; ConnectPool::ConnectPool() { hostName = "192.168.1.126"; databaseName = "source_data"; username = "root"; password = "zhuxiaoyong1212"; databaseType = "QMYSQL"; testOnBorrow = true; testOnBorrowSql = "SELTCT 1"; maxWaitTime = 1000; waitInterval = 200; //嘗試獲取連接時等待間隔時間 maxConnectionCount = 5; } ConnectPool::~ConnectPool() { foreach (QString connectionName, usedConnectionNames) { QSqlDatabase::removeDatabase(connectionName); } foreach (QString connectionName, unusedConnectionNames) { QSqlDatabase::removeDatabase(connectionName); } } ConnectPool &ConnectPool::getInstance() { if(instance==nullptr) { QMutexLocker locker(&mutex); if(nullptr==instance) { instance = new ConnectPool(); } } return *instance; } void ConnectPool::release() { QMutexLocker locker(&mutex); delete instance; //調用析構函數 instance = nullptr; } QSqlDatabase ConnectPool::openConnection() { ConnectPool &pool = ConnectPool::getInstance(); QString connectionName; QMutexLocker locker(&mutex); //已創建的連接數 int connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size(); //如果連接已經用完,等待waitInterval毫秒,看是否有可用連接,最大等待maxWaitTime毫秒 for(int i=0;i<pool.maxWaitTime&&pool.unusedConnectionNames.size()==0&&connectionCount == pool.maxConnectionCount;i+=pool.waitInterval) { waitConnection.wait(&mutex,pool.waitInterval); //重新計算已創建連接數 connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size(); } if(pool.unusedConnectionNames.size()>0) { //有已經回收的連接,復用它們 connectionName = pool.unusedConnectionNames.dequeue(); } else if(connectionCount < pool.maxConnectionCount) { //沒有已經回收的連接,但是沒有達到最大連接數,則創建新連接 connectionName = QString("Connection-%1").arg(connectionCount+1); } else { //已經達到最大連接數 qDebug()<<"Cannot create more connections"; return QSqlDatabase(); } //創建連接 QSqlDatabase db = pool.createConnection(connectionName); //有效連接才放入usedConnectionNames if(db.isOpen()) { pool.usedConnectionNames.enqueue(connectionName); } return db; } void ConnectPool::closeConnection(QSqlDatabase connection) { ConnectPool &pool = ConnectPool::getInstance(); QString connectName = connection.connectionName();
QMutexLocker locker(&mutex);//位置別放錯了 //如果是我們創建的連接,從used里刪除,放入unused if(pool.usedConnectionNames.contains(connectName)) { pool.usedConnectionNames.removeOne(connectName); pool.unusedConnectionNames.enqueue(connectName); waitConnection.wakeOne(); } } QSqlDatabase ConnectPool::createConnection(const QString &connectionName) { //連接已經創建過的,復用它,而不是重新創建 if(QSqlDatabase::contains(connectionName)) { QSqlDatabase db1 = QSqlDatabase::database(connectionName); if(testOnBorrow) { qDebug()<<"Test connection on borrow, execute"<<testOnBorrowSql<<",for"<<connectionName; QSqlQuery query(testOnBorrowSql,db1); if(query.lastError().type()!=QSqlError::NoError&&!db1.open()) { qDebug()<<"Open database error:"<<db1.lastError().text(); return QSqlDatabase(); } } return db1; } if(!QSqlDatabase::contains(connectionName)) { QSqlDatabase db = QSqlDatabase::addDatabase(databaseType,connectionName); db.setHostName(hostName); db.setDatabaseName(databaseName); db.setUserName(username); db.setPassword(password); if(!db.open()) { qDebug()<<"Open database error:"<<db.lastError().text(); return QSqlDatabase(); } return db; } }
注意:
1、這里的原理是仿照線程池的原理,用兩個隊列來存使用的和沒使用的數據庫連接名,然后在各自的地方或線程處使用這個連接名。這里有個問題:可能會跨線程調用數據庫連接,注意,經過測試,5.12版本到5.13版本一直不支持這個操作,無奈又退到5.10。看過外帖子,貌似是Qt自己的bug,但是至今2019.09.06沒修復。
2、在使用了Qt5.13版本時,發現Qt根本沒有QMysql驅動,即打印當前可用數據庫驅動沒有MySql【按理說是文章開頭那張表】。
3.3、自己封裝的數據庫連接池,借鑒了上面的
MyConnection.h
#ifndef MYCONNECTION_H #define MYCONNECTION_H #define MYDBP_VALUE_0 0 #include <QDebug> #include <QSqlDatabase> #include <QSqlError> #include <QQueue> #include <mutex> #include <condition_variable> #include "mymethod.h" #include "includes.h" class MyConnection { public: static MyConnection* getInstace(); static QSqlDatabase getDb();//獲取一個db static void removeDb(QSqlDatabase db);//回收一個db static void clearAllDb();//清楚所有連接 void setCfg(DbConfig cfg); private: MyConnection(); static MyConnection* singleton; ~MyConnection(); private://運算相關 QQueue<QString> freeQueue,busyQueue;//可用連接隊列,繁忙隊列 static std::mutex m_mutex; static std::condition_variable cv; private://數據庫配置相關 QString dbIp; QString dbName; QString userName; QString password; QString dbType; int maxConnectionCount; // 最大連接數 DbConfig cfg; }; #endif // MYCONNECTION_H
MyConnection.cpp
#include "myconnection.h" /*****************************************************************/ //作者:朱小勇 //函數名稱:構造函數 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ MyConnection::MyConnection() { Mymethod::record("construct dp connection pool.",PRINT_INFO); dbIp = "192.168.1.1"; dbName = "ea_phm"; userName = "root"; password = "zhuxiaoyong1212"; dbType = "QMYSQL"; maxConnectionCount = DB_CONNET_COUNT;// 最大連接數 } /*****************************************************************/ //作者:朱小勇 //函數名稱:數據庫設置 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ void MyConnection::setCfg(DbConfig cfg) { dbIp = cfg.hostIp; dbName = cfg.databaseName; userName = cfg.username; password = cfg.password; dbType = "QMYSQL"; Mymethod::record("db connection initialized,database name:"+dbName,PRINT_INFO); } /*****************************************************************/ //作者:朱小勇 //函數名稱:析構函數 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ MyConnection::~MyConnection() { clearAllDb(); if(singleton != nullptr) { delete singleton; singleton = nullptr; } } MyConnection* MyConnection::singleton = new MyConnection();//靜態變量初始化 std::mutex MyConnection::m_mutex; std::condition_variable MyConnection::cv; /*****************************************************************/ //作者:朱小勇 //函數名稱:返回單例對象 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ MyConnection* MyConnection::getInstace() { return singleton; } /*****************************************************************/ //作者:朱小勇 //函數名稱:獲取一個連接 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ QSqlDatabase MyConnection::getDb() { std::unique_lock<std::mutex> lck(m_mutex); MyConnection* instace = MyConnection::getInstace(); int currentConnets = instace->freeQueue.size()+instace->busyQueue.size(); if(currentConnets < instace->maxConnectionCount)//新建連接 { QSqlDatabase db = QSqlDatabase::addDatabase(instace->dbType,QString("connet_num_%1").arg(currentConnets)); db.setHostName(instace->dbIp); db.setDatabaseName(instace->dbName); db.setUserName(instace->userName); db.setPassword(instace->password); if(!db.open()) { QSqlError tempErr = db.lastError(); Mymethod::record(tempErr.text(),PRINT_ERR); return QSqlDatabase(); } if(db.isValid()&&db.isOpen()) { instace->busyQueue.push_back(db.connectionName()); return db; } } if(instace->freeQueue.size() > MYDBP_VALUE_0)//有空閑的連接直接使用 { QSqlDatabase db = QSqlDatabase::database(instace->freeQueue.front()); db.setHostName(instace->dbIp); db.setDatabaseName(instace->dbName); db.setUserName(instace->userName); db.setPassword(instace->password); if(!db.open()) { QSqlError tempErr = db.lastError(); Mymethod::record(tempErr.text(),PRINT_ERR); return QSqlDatabase(); } if(db.isValid()&&db.isOpen()) { instace->busyQueue.push_back(db.connectionName());//存入繁忙隊列 instace->freeQueue.pop_front();//從空閑隊列刪除 return db; } } if((instace->freeQueue.size()==MYDBP_VALUE_0)&&(instace->busyQueue.size()==instace->maxConnectionCount))//無空閑的連接,等待外部釋放 { Mymethod::record(QString("cannot create more connections,used db connection:%1,unused db connection:%2").arg(instace->busyQueue.size()).arg(instace->freeQueue.size()),PRINT_ERR); while(instace->freeQueue.size()==MYDBP_VALUE_0) { cv.wait(lck); } QSqlDatabase db = QSqlDatabase::addDatabase(instace->dbType,instace->freeQueue.front()); db.setHostName(instace->dbIp); db.setDatabaseName(instace->dbName); db.setUserName(instace->userName); db.setPassword(instace->password); if(!db.open()) { QSqlError tempErr = db.lastError(); Mymethod::record(tempErr.text(),PRINT_ERR); return QSqlDatabase(); } if(db.isValid()&&db.isOpen()) { instace->busyQueue.push_back(db.connectionName());//存入繁忙隊列 instace->freeQueue.pop_front();//從空閑隊列刪除 return db; } } return QSqlDatabase(); } /*****************************************************************/ //作者:朱小勇 //函數名稱:刪除連接 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ void MyConnection::removeDb(QSqlDatabase db) { std::unique_lock<std::mutex> lck(m_mutex); MyConnection* instace = MyConnection::getInstace(); if(instace->busyQueue.contains(db.connectionName())) { instace->busyQueue.removeOne(db.connectionName()); instace->freeQueue.push_back(db.connectionName()); } cv.notify_one(); } /*****************************************************************/ //作者:朱小勇 //函數名稱:清除所有連接 //函數參數:NULL //函數返回值:NULL //函數作用:NULL //備注:NULL /*****************************************************************/ void MyConnection::clearAllDb() { std::unique_lock<std::mutex> lck(m_mutex); MyConnection* instace = MyConnection::getInstace(); for(auto connct : instace->freeQueue) { QSqlDatabase::removeDatabase(connct); } for(auto connct : instace->busyQueue) { QSqlDatabase::removeDatabase(connct); } }
我自己封裝這個相比於網上廣為流傳的,少了等待操作。所以當外部向這個單例類請求獲取db后,如果返回的是空db,應該再外面自行等待
QSqlDatabase db = MyConnection::getDb(); while(!db.isValid() || !db.isOpen()) { Mymethod::record("db is invalid,retry to get a valid db.",PRINT_ERR); QThread::msleep(VALUE_300); db = MyConnection::getDb(); } QSqlQuery q(db); if(!q.exec(sql)) { QSqlError tempErr = q.lastError(); Mymethod::record(tempErr.text()+" sql:"+sql,PRINT_ERR); } else { Mymethod::record("insert "+db.databaseName()+" ok.",PRINT_OK); } MyConnection::removeDb(db);
2019.10.24:
今天出現了個問題,剛好是程序員節,md調試了了一天。
問題描述:
線程A不斷產生sql語句,需要讓兩個數據庫分別執行這個sql語句。所以在線程A中建立兩個子線程B和C,分別對應兩個數據庫。然后發現兩個線程同時啟動【即兩個線程同時調Qt訪問mysql的dll】會報錯:
解決:
在其中一個子線程中初次調用的時候延時1s:
static bool test=true; if(test) { QThread::sleep(1); test=false; }