Qt:QSqlDatabase的進一步封裝(多執行緒支援+更加簡單的操作)
開發背景:
1.直接用QSqlDatabase我覺得太麻煩了;
2.對於某些資料庫,多個執行緒同時使用一個QSqlDatabase的時候會崩潰;
3.這段時間沒什麼乾貨放出來覺得渾身不舒服,就想寫一個。
於是,我就封裝了一下
只要簡單的例項化,然後通過query()就可以拿到QSqlQuery的例項化物件。
還自帶計時,一段時間不用可自動關閉,既保證效率也不佔用無用資源。
注:需要C++11的支援
不多說,上程式碼:
JasonQt_Database.h
#ifndef __JasonQt_Database_h__ #define __JasonQt_Database_h__ // C++ lib import #include <functional> // Qt lib import #include <QtCore> #include <QtSql> #define PropertyDeclare(Type, Name, setName, ...) \ private: \ Type m_ ## Name __VA_ARGS__; \ public: \ inline const Type &Name(void) const { return m_ ## Name; } \ inline void setName(const Type &Name) { m_ ## Name = Name; } \ private: namespace JasonQt_Database { enum DatabaseModeEnum{ DatabaseNameMode, DatabaseHostMode }; enum QueryMode { QueryAutoMode, QueryMultiMode, QuerySingleMode }; class DatabaseSettings { private: PropertyDeclare(DatabaseModeEnum, databaseMode, setDatabaseMode) PropertyDeclare(QString, databaseType, setDatabaseType) PropertyDeclare(QString, connectionName, setConnectionName) // File mode PropertyDeclare(QString, nameModeName, setNameModeName) // Host mode PropertyDeclare(QString, hostModeHostName, setHostModeHostName) PropertyDeclare(QString, hostModeDatabaseName, setHostModeDatabaseName) PropertyDeclare(QString, hostModeUserName, setHostModeUserName) PropertyDeclare(QString, hostModePassword, setHostModePassword) private: DatabaseSettings(const DatabaseModeEnum &databastMode, const QString &databaseType, const QString &connectionName); public: DatabaseSettings(const QString &databaseType, const QString &connectionName, const QString &nameModeName); DatabaseSettings(const QString &databaseType, const QString &connectionName, const QString &hostModeHostName, const QString &hostModeDatabaseName, const QString &hostModeUserName, const QString &hostModePassword); }; class ConnectSettings { private: PropertyDeclare(int, maxOpenTime, setMaxOpenTime) PropertyDeclare(QueryMode, queryMode, setQueryMode) PropertyDeclare(int, minWaitTime, setMinWaitTime) public: ConnectSettings(const int &maxOpenTime = 60 * 1000, const QueryMode &queryMode = QueryAutoMode, const int &minWaitTime = -1); }; class Query { private: QSqlQuery *m_query; QMutex *m_mutex = NULL; public: Query(QSqlDatabase &dataBase, QMutex *mutex = NULL); Query(Query &&other); ~Query(void); inline QSqlQuery *operator->(void) { return m_query; } inline QSqlQuery *operator*(void) { return m_query; } QSqlQuery *takeQuery(void); QMutex *takeMutex(void); }; class ConnectNode: public QObject { Q_OBJECT private: QSqlDatabase *m_database = NULL; QString m_connectionName; DatabaseSettings m_dataBaseSettings; ConnectSettings m_connectSettings; QTimer *m_autoClose = NULL; QMutex *m_mutex = NULL; public: ConnectNode(const DatabaseSettings &dataBaseSettings, const ConnectSettings &connectSettings); ~ConnectNode(void); public: Query query(void); public slots: bool addDataBase(void); void removeDataBase(void); bool open(void); void close(void); signals: void controlStartAutoClose(void); void controlStopAutoClose(void); }; class Control { private: DatabaseSettings m_databaseSettings; ConnectSettings m_connectSettings; QMap<qint64, ConnectNode *> m_node; QMutex m_mutex; QTime *m_wait = NULL; public: Control(const DatabaseSettings &databaseSettings, const ConnectSettings &connectSettings = ConnectSettings()); Control(const Control &) = delete; ~Control(void); public: void removeAll(void); Query query(void); private: void insert(const qint64 &key); }; } #endif//__JasonQt_Database_h__
JasonQt_Database.cpp
#include "JasonQt_Database.h" using namespace JasonQt_Database; // DatabaseSettings DatabaseSettings::DatabaseSettings(const DatabaseModeEnum &databastMode, const QString &databaseType, const QString &connectionName) { m_databaseMode = databastMode; m_databaseType = databaseType; m_connectionName = connectionName; } DatabaseSettings::DatabaseSettings(const QString &databaseType, const QString &connectionName, const QString &nameModeName): DatabaseSettings(DatabaseNameMode, databaseType, connectionName) { m_nameModeName = nameModeName; } DatabaseSettings::DatabaseSettings(const QString &databaseType, const QString &connectionName, const QString &hostModeHostName, const QString &hostModeDatabaseName, const QString &hostModeUserName, const QString &hostModePassword): DatabaseSettings(DatabaseHostMode, databaseType, connectionName) { m_hostModeHostName = hostModeHostName; m_hostModeDatabaseName = hostModeDatabaseName; m_hostModeUserName = hostModeUserName; m_hostModePassword = hostModePassword; } // ConnectSettings ConnectSettings::ConnectSettings(const int &maxOpenTime, const QueryMode &queryMode, const int &minWaitTime) { m_maxOpenTime = maxOpenTime; m_queryMode = queryMode; m_minWaitTime = minWaitTime; } // Query Query::Query(QSqlDatabase &dataBase, QMutex *mutex): m_query(new QSqlQuery(dataBase)) { m_mutex = mutex; } Query::Query(Query &&other) { m_query = other.takeQuery(); m_mutex = other.takeMutex(); } Query::~Query(void) { if(m_query) { delete m_query; } if(m_mutex) { m_mutex->unlock(); } } QSqlQuery *Query::takeQuery(void) { auto buf = m_query; m_query = NULL; return buf; } QMutex *Query::takeMutex(void) { auto buf = m_mutex; m_mutex = NULL; return buf; } // ConnectNode ConnectNode::ConnectNode(const DatabaseSettings &dataBaseSettings, const ConnectSettings &connectSettings): m_dataBaseSettings(dataBaseSettings), m_connectSettings(connectSettings) { m_connectionName = QString("%1(%2)").arg(m_dataBaseSettings.connectionName()).arg(QString::number(qint64(QThread::currentThread()), 16)); m_mutex = new QMutex(QMutex::Recursive); if(m_connectSettings.maxOpenTime()) { m_autoClose = new QTimer; m_autoClose->setSingleShot(true); m_autoClose->setInterval(m_connectSettings.maxOpenTime()); m_autoClose->moveToThread(qApp->thread()); m_autoClose->setParent(qApp); connect(m_autoClose, SIGNAL(timeout()), this, SLOT(close()), Qt::DirectConnection); connect(this, SIGNAL(controlStartAutoClose()), m_autoClose, SLOT(start())); connect(this, SIGNAL(controlStopAutoClose()), m_autoClose, SLOT(stop())); } this->addDataBase(); } ConnectNode::~ConnectNode(void) { if(m_mutex){ m_mutex->lock(); } if(m_autoClose) { m_autoClose->deleteLater(); } this->removeDataBase(); if(m_mutex){ m_mutex->unlock(); } if(m_mutex){ delete m_mutex; } } Query ConnectNode::query(void) { if(!m_database) { this->addDataBase(); } if(!m_database->isOpen()) { m_database->open(); } if(m_mutex){ m_mutex->lock(); } Query buf(*m_database, m_mutex); emit controlStartAutoClose(); return buf; } bool ConnectNode::addDataBase(void) { if(m_mutex){ m_mutex->lock(); } if(m_database) { this->removeDataBase(); } m_database = new QSqlDatabase(QSqlDatabase::addDatabase(m_dataBaseSettings.databaseType(), m_connectionName)); switch(m_dataBaseSettings.databaseMode()) { case DatabaseNameMode: { m_database->setDatabaseName(m_dataBaseSettings.nameModeName()); break; } case DatabaseHostMode: { m_database->setHostName(m_dataBaseSettings.hostModeHostName()); m_database->setDatabaseName(m_dataBaseSettings.hostModeDatabaseName()); m_database->setUserName(m_dataBaseSettings.hostModeUserName()); m_database->setPassword(m_dataBaseSettings.hostModePassword()); break; } default: { if(m_mutex){ m_mutex->unlock(); } return false; } } const auto &&flag = this->open(); if(m_mutex){ m_mutex->unlock(); } return flag; } void ConnectNode::removeDataBase(void) { if(m_mutex){ m_mutex->lock(); } delete m_database; m_database = NULL; QSqlDatabase::removeDatabase(m_connectionName); if(m_mutex){ m_mutex->unlock(); } } bool ConnectNode::open(void) { if(!m_database) { this->addDataBase(); } if(m_mutex){ m_mutex->lock(); } emit controlStartAutoClose(); const auto &&Flag = m_database->open(); if(m_mutex){ m_mutex->unlock(); } return Flag; } void ConnectNode::close(void) { if(m_mutex) { if(m_mutex->tryLock()) { m_mutex->unlock(); emit controlStopAutoClose(); m_database->close(); } else { emit controlStartAutoClose(); } } else { emit controlStopAutoClose(); m_database->close(); } } // Control Control::Control(const DatabaseSettings &databaseSettings, const ConnectSettings &connectSettings): m_databaseSettings(databaseSettings), m_connectSettings(connectSettings) { if(m_connectSettings.queryMode() == QueryAutoMode) { if(databaseSettings.databaseType() == "QMYSQL") { m_connectSettings.setQueryMode(QueryMultiMode); } else if(databaseSettings.databaseType() == "QODBC") { m_connectSettings.setQueryMode(QueryMultiMode); } else { m_connectSettings.setQueryMode(QuerySingleMode); } } if(m_connectSettings.queryMode() == QuerySingleMode) { this->insert(qint64(QThread::currentThread())); } if(m_connectSettings.minWaitTime() == -1) { if(databaseSettings.databaseType() == "QMYSQL") { m_connectSettings.setMinWaitTime(0); } else if(databaseSettings.databaseType() == "QODBC") { m_connectSettings.setMinWaitTime(0); } else { m_connectSettings.setMinWaitTime(5); m_wait = new QTime; m_wait->start(); } } else { m_wait = new QTime; m_wait->start(); } } Control::~Control(void) { for(auto &now: m_node) { now->deleteLater(); } if(m_wait) { delete m_wait; } } void Control::removeAll(void) { m_mutex.lock(); for(auto &Now: m_node) { Now->removeDataBase(); } m_mutex.unlock(); } Query Control::query(void) { if(m_wait) { const auto &&flag = m_connectSettings.minWaitTime() - m_wait->elapsed(); m_wait->restart(); if(flag > 0) { QThread::msleep(flag); } } if(m_connectSettings.queryMode() == QueryMultiMode) { m_mutex.lock(); const auto &¤tThread = qint64(QThread::currentThread()); const auto &&now = m_node.find(currentThread); if(now != m_node.end()) { auto buf((*now)->query()); m_mutex.unlock(); return buf; } else { this->insert(qint64(QThread::currentThread())); m_mutex.unlock(); return this->query(); } } else { return (*m_node.begin())->query(); } } void Control::insert(const qint64 &key) { m_node[key] = new ConnectNode(m_databaseSettings, m_connectSettings); }
使用:
// Qt lib import #include <QCoreApplication> #include <QtConcurrent> #include <QSqlError> // JasonQt lib import #include "JasonQt/JasonQt_Database.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); /* * 注:關於附加引數 * 這是可以不寫的,如果要寫的話,可以參考這個: * * 單次開啟資料庫最大時間:也就是最大open的時間,對於某些資料庫,長時間open但不使用,不僅會造成資源浪費還會意外斷開。在設定了60 * 1000後,且60秒內未進行查詢,就自動斷開。 * 多執行緒支援:簡單的說就是高階點的資料庫,比如 MySql 寫 JasonQt_Database::QueryMultiMode ;低階的,比如 Sqlite ,寫 JasonQt_Database::QuerySingleMode ,就可以了。 * 最小等待時間:對於某些資料庫,比如Sqlite,密集查詢時可能出錯,此時可以適當的提升兩次查詢之間的最小等待時間,比如10ms */ // Sqlite的連線方式 型別 連線名 Sqlite檔案路徑 單次開啟資料庫最大時間 多執行緒支援 最小等待時間 JasonQt_Database::Control control({ "QSQLITE", "TestDB", "/Users/Jason/test.db" }, { 60 * 1000, JasonQt_Database::QuerySingleMode, 10}); // MySql的連線方式 型別 連線名 IP 資料庫 使用者名稱 密碼 // JasonQt_Database::Control control({ "QMYSQL", "TestDB", "localhost", "JasonDB", "root", "YourPassword" }); // SqlServer的連線方式 型別 連線名 資料庫 資料庫名 使用者名稱 密碼 // JasonQt_Database::Control control({ "QODBC", "TestDB", "Driver={SQL SERVER};server=iZ23kn6vmZ\\TEST;database=test;uid=sa;pwd=YourPassword;" }); auto insert = [&]() { auto query(control.query()); // 這裡的query在解引用( -> 或者 * )後返回的是 QSqlQuery ,直接用就可以了,不需要單獨開啟資料庫或者其他的初始化 query->prepare("insert into Test1 values(?)"); // 模擬插入操作 query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } query->clear(); query->prepare("insert into Test2 values(NULL, ?, ?)"); query->addBindValue(rand() % 1280); QString buf; for(int now = 0; now < 50; now++) { buf.append('a' + (rand() % 26)); } query->addBindValue(buf); if(!query->exec()) { qDebug() << "Error" << __LINE__; } }; auto delete_ = [&]() { auto query(control.query()); query->prepare("delete from Test1 where data = ?"); query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } query->clear(); query->prepare("delete from Test2 where data1 = ?"); query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } }; auto update = [&]() { auto query(control.query()); query->prepare("update Test1 set data = ? where data = ?"); query->addBindValue(rand() % 1280); query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } query->clear(); query->prepare("update Test2 set data1 = ?, data2 = ? where data1 = ?"); query->addBindValue(rand() % 1280 + 1); QString buf; for(int now = 0; now < 50; now++) { buf.append('a' + (rand() % 26)); } query->addBindValue(buf); query->addBindValue(rand() % 1280 + 1); if(!query->exec()) { qDebug() << "Error" << __LINE__; } }; auto select = [&]() { auto query(control.query()); query->prepare("select * from Test1 where data = ?"); query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } query->clear(); query->prepare("select * from Test2 where data1 = ?"); query->addBindValue(rand() % 1280); if(!query->exec()) { qDebug() << "Error" << __LINE__; } }; volatile int count = 0, last = 0; QTime time; time.start(); QThreadPool::globalInstance()->setMaxThreadCount(10); for(int now = 0; now < 3; now++) // 開啟3個執行緒測試 { QtConcurrent::run([&]() { while(true) { count++; if(!(count % 1000)) { const auto &&now = time.elapsed(); qDebug() << now - last; // 列印每完成1000語句的時間 last = now; } switch(rand() % 20) { case 0: { insert(); break; } case 1: { delete_(); break; } case 2: { update(); break; } default: { select(); break; } } } QThread::msleep(10); }); } return a.exec(); }
我也寫了一個示例工程,可以前往這裡下載
相關推薦
Qt:QSqlDatabase的進一步封裝(多執行緒支援+更加簡單的操作)
開發背景: 1.直接用QSqlDatabase我覺得太麻煩了; 2.對於某些資料庫,多個執行緒同時使用一個QSqlDatabase的時候會崩潰; 3.這段時間沒什麼乾貨放出來覺得渾身不舒服,就想寫一個。 於是,我就封裝了一下 只要簡單的例項化,然後通過query()就可以
java個人學習筆記16(多執行緒+extends Thread+implements Runnable)
1.多執行緒 程序:正在執行的應用程式在記憶體中分配的空間 執行緒:是程序中負責程式執行的執行單元,也稱執行路徑 一個程序中至少有一個執行緒在負責該程序的執行 多執行緒技術:解決多部分程式碼同時執行的需求,合理使用cpu資源,提高使用者體驗。(微觀上序列,並未實際上提高效率
python程式設計(多執行緒c回撥python)
【 宣告:版權所有,歡迎轉載,請勿用於商業用途。 聯絡信箱:feixiaoxing @163.com】 python下面的GIL決定了每次thread執行的時候不能實現完全的併發執行。所以如果多執行緒c呼叫python程式碼的時候,有很多地方需要注意一
刨根問底系列(3)——關於socket api的原子操作性和執行緒安全性的探究和實驗測試(多執行緒同時send,write)
多個執行緒對同一socket同時進行send操作的結果 1. 概覽 1.1 起因 自己寫的專案裡,為了保證連線不中斷,我起一個執行緒專門傳送心跳包保持連線,那這個執行緒在send傳送資料時,可能會與主執行緒中的send衝突,因此我就想探討一下socket api是否具有執行緒安全性。網上很多說法,但多是推測,
Qt:Qt實現Winsock網路程式設計—TCP服務端和客戶端通訊(多執行緒)
Qt實現Winsock網路程式設計—TCP服務端和客戶端通訊(多執行緒) 前言 感覺Winsock網路程式設計的api其實和Linux下網路程式設計的api非常像,其實和其他程式語言的網路程式設計都差不太多。博主用Qt實現的,當然不想用黑視窗唄,有介面可以看到,由於GUI程式設計
機器學習筆記(十九):TensorFlow實戰十一(多執行緒輸入資料)
1 - 引言 為了加速模型訓練的時間,TensorFlow提供了一套多執行緒處理輸入資料的框架。 下面我們來詳細的介紹如何使用多執行緒來加速我們的模型訓練速度 2 - 佇列與多執行緒 在TensorFlow中,佇列和變數類似,我們可以修改它們的狀態。下面給出一個示例來展示如
多執行緒學習(4):三種實現Java多執行緒的方法:Thread、Callable和Runable 的比較與區別
2018年10月03日 目錄 前言 前言 JVM允許應用程式併發執行多執行緒:最常用的是兩個方法:(1)基礎Thread類,重寫run()方法;(2)或實現Runnable 介面,實現介面的run()方法;(3)另外一種方法是:實現callable 介面
QT實現哈夫曼壓縮(多執行緒)
本人菜雞程式設計師一枚,最近剛剛學習的資料結構中的哈夫曼樹,就用QT寫了一個哈夫曼壓縮,話不多說先上步驟,再上程式碼。(如果有更好的想法,歡迎指點) 1.先寫出建最小堆和建哈夫曼樹程式碼(建最小堆的程式碼可以通過STL中的堆代替) 2.寫出壓縮類的程式碼,類中
【Java併發程式設計】之六:Runnable和Thread實現多執行緒的區別(含程式碼)
Java中實現多執行緒有兩種方法:繼承Thread類、實現Runnable介面,在程式開發中只要是多執行緒,肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Th
java多執行緒:5、Java對多執行緒的支援(二)執行緒優先順序
一、執行緒優先順序 在java當中,每一個執行緒都有一個優先順序,我們可以通過Thread當中的getPriority()方法、setPriority方法去得到一個執行緒的優先順序和設定一個執行緒的優先順序。 設定執行緒優先順序,它的引數是一個整形。最小為1(Thread.M
java多執行緒:4、Java對多執行緒的支援(二)後臺執行緒setDaemon、暫停執行緒yield
文章目錄 一、設定後臺執行緒 二、yield方法,暫停執行緒讓別的執行緒執行 上篇文章介紹了執行緒實現方式、執行緒的隨機性,大家如需瞭解可參考 java多執行緒:3、Java對多執行緒的支援(一)執行緒實現方式、執行緒的隨機性 這篇我們來看看後臺執行緒setD
TensorFlow學習系列(五):如何使用佇列和多執行緒優化輸入管道
這篇教程是翻譯Morgan寫的TensorFlow教程,作者已經授權翻譯,這是原文。 目錄 TensorFlow 1.0版本已經出來了,隨著這次更新,一些不錯的指導建議出現在官網上面。其中一個我比較關心的是 f
【原創】中文分詞系統 ICTCLAS2015 的JAVA封裝和多執行緒執行(附程式碼)
本文針對的問題是 ICTCLAS2015 的多執行緒分詞,為了實現多執行緒做了簡單的JAVA封裝。如果有需要可以自行進一步封裝其它介面。 首先ICTCLAS2015的傳送門(http://ictclas.nlpir.org/),其對中文分詞做的比較透徹,而且有一定的可調式性。但是應用到實際開發中的話
JAVA多執行緒入門(二):JAVA中如何寫多執行緒
第一種方式:繼承Thread 步驟: 1.建立執行緒類,繼承自Thread + 重寫run,run中寫執行緒體,執行緒體就是mian()函式裡面的寫法 2.使用執行緒: 2.1 建立執行緒物件 2.2 執行緒物件.start() 步驟展示: 1. public
qt匯出、操作excel(多執行緒)
使用QAxObject在多執行緒下進行excel操作,將原來固定格式的文字檔案,通過解析之後寫入到excel中。 效果圖 多執行緒使用 Worker *pWorker = new Worker(); connect(pWorker, SIGNAL(err
Qt實用技巧:QPainterPath繪圖路徑(多次畫同樣的圖形集合)
需求 根據配置檔案,可不改變程式只調整配置檔案可調整主頁面上的字串。 原理 1.讀取檔案,固定格式(檔案在本文章中省略) 2.寫一串字元,使用QPainterPath 3.注意QPainter的時候,需
C#多執行緒基礎(多執行緒的優先順序、狀態、同步)
一、關於多執行緒的優先順序、狀態、同步指令碼如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System
iOS - 知識梳理(多執行緒)
多執行緒:一個程序裡面開啟多條執行緒,每條執行緒可以單獨的執行不同的任務。 iOS實現多執行緒的方式: 1、pthread(C寫的、基本不用) 2、NSThread 3、gcd 4、NSOperation 下面分別介紹下後三個常用的多執行緒方式 NSThread: 使用方式
c++封裝win32多執行緒簡介
/* 執行緒函式如果宣告在類中, 則需要靜態函式. 否則普通函式中將壓入this引數 . 所以在類中先宣告一個靜態函式,用於執行緒的啟動. 再寫一個虛擬函式 run , 線上程中呼叫, 也可以用於子類的重寫 */ class ThreadObj
購物表(動態連結串列)+鬧鐘提醒(多執行緒)
基本連結串列的應用 增 刪 查 找 排 模糊查詢 核心程式碼如下: #include <stdio.h> #include <math.h> #include <string.h> #include <malloc.h> #incl