小優(yōu)智能科技有限公司成立于2015年底,是一家專注于高精度3D機(jī)器視覺(jué)模組研發(fā)、生產(chǎn)及銷售的高科技企業(yè)。
公司自主研發(fā)的3D機(jī)器視覺(jué)模組采用激光/DLP白光編碼光柵結(jié)構(gòu)光+雙工業(yè)相機(jī)方案,還原物體三維信息,廣泛應(yīng)用于消費(fèi)電子領(lǐng)域、工業(yè)領(lǐng)域和安防領(lǐng)域,具有精度高、速度快、成本低的優(yōu)勢(shì)。
基于C++語(yǔ)言實(shí)現(xiàn)Qt框架下的數(shù)據(jù)庫(kù)連接池應(yīng)用
隨著互聯(lián)網(wǎng)和大數(shù)據(jù)時(shí)代的到來(lái),數(shù)據(jù)庫(kù)操作已經(jīng)成為許多企業(yè)和應(yīng)用程序不可或缺的重要部分。數(shù)據(jù)庫(kù)連接池技術(shù)的應(yīng)用,可以提高數(shù)據(jù)庫(kù)使用效率,減少資源和時(shí)間的浪費(fèi)?;贑++語(yǔ)言的Qt框架,也可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池技術(shù),本文將介紹如何使用Qt實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池,讓你的數(shù)據(jù)庫(kù)操作更加高效。
一、什么是數(shù)據(jù)庫(kù)連接池
數(shù)據(jù)庫(kù)連接池是一種通過(guò)預(yù)先建立多個(gè)數(shù)據(jù)庫(kù)連接,在應(yīng)用程序的運(yùn)行過(guò)程中重復(fù)利用數(shù)據(jù)庫(kù)連接的技術(shù)。通俗的說(shuō),就是在應(yīng)用程序中預(yù)先建立多個(gè)數(shù)據(jù)庫(kù)連接,當(dāng)需要訪問(wèn)數(shù)據(jù)庫(kù)時(shí)從連接池中獲取一個(gè)數(shù)據(jù)庫(kù)連接,用完后還回連接池中。這種利用池化技術(shù)的方式能夠降低新建連接的時(shí)間和資源消耗,提高數(shù)據(jù)庫(kù)的操作效率。下面,我們將使用Qt實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池。
二、Qt中數(shù)據(jù)庫(kù)連接的建立與使用
在Qt中,數(shù)據(jù)庫(kù)連接的建立與使用非常簡(jiǎn)單。首先需要?jiǎng)?chuàng)建一個(gè)QSqlDatabase實(shí)例,在該實(shí)例中設(shè)置需要連接的數(shù)據(jù)庫(kù)類型、主機(jī)和用戶名等信息。然后,使用QSqlDatabase::open()函數(shù)打開(kāi)該數(shù)據(jù)庫(kù)連接,并進(jìn)行一些操作。
#include <QSqlDatabase >
#include <QSqlQuery>
#include <QDebug>
QSqlDatabase db = QSqlDatabase::addDatabase(“QMYSQL”);
db.setHostName(“l(fā)ocalhost”);
db.setDatabaseName(“test”);
db.setUserName(“root”);
db.setPassword(“password”);
if(db.open())
{
qDebug() << “Open database success !”;
QSqlQuery query(db);
query.exec(“select * from testDB”);
while(query.next())
qDebug() << query.value(0).toString();
}
上述代碼演示了在Qt中連接MySQL數(shù)據(jù)庫(kù)的過(guò)程。首先使用QSqlDatabase::addDatabase(“QMYSQL”)函數(shù)創(chuàng)建一個(gè)QSqlDatabase實(shí)例,在該實(shí)例中設(shè)置需要連接的數(shù)據(jù)庫(kù)類型、主機(jī)和用戶名等信息。然后,使用QSqlDatabase::open()函數(shù)打開(kāi)該數(shù)據(jù)庫(kù)連接。在數(shù)據(jù)庫(kù)連接成功后,使用QSqlQuery實(shí)例進(jìn)行一些操作。注意,一定要在使用完QSqlQuery對(duì)象后,調(diào)用其~QSqlQuery()函數(shù)釋放資源。
三、Qt實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池
下面,我們將基于Qt實(shí)現(xiàn)一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)連接池。創(chuàng)建一個(gè)ConnectionPool類,該類中包含了多個(gè)數(shù)據(jù)庫(kù)連接對(duì)象。ConnectionPool的.h文件內(nèi)容如下:
#ifndef CONNECTIONPOOL_H
#define CONNECTIONPOOL_H
#include <QtSql>
#include <QQueue>
#include <QString>
#include <QMutex>
#include <QMutexLocker>
#include<QDebug>
#include<QSettings>//配置文件
class ConnectionPool {
public:
static void release(); // 關(guān)閉所有的數(shù)據(jù)庫(kù)連接
static QSqlDatabase openConnection(); // 獲取數(shù)據(jù)庫(kù)連接
static void closeConnection(QSqlDatabase connection); // 釋放數(shù)據(jù)庫(kù)連接回連接池
~ConnectionPool();
private:
static ConnectionPool& getInstance();
ConnectionPool();
ConnectionPool(const ConnectionPool &other);
ConnectionPool& operator=(const ConnectionPool &other);
QSqlDatabase createConnection(const QString &connectionName); // 創(chuàng)建數(shù)據(jù)庫(kù)連接
QQueue<QString> usedConnectionNames; // 已使用的數(shù)據(jù)庫(kù)連接名
QQueue<QString> unusedConnectionNames; // 未使用的數(shù)據(jù)庫(kù)連接名
// 數(shù)據(jù)庫(kù)信息
QString hostName;
QString databaseName;
QString username;
QString password;
QString databaseType;
bool testOnBorrow; // 取得連接的時(shí)候驗(yàn)證連接是否有效
QString testOnBorrowSql; // 測(cè)試訪問(wèn)數(shù)據(jù)庫(kù)的 SQL
int maxWaitTime; // 獲取連接最大等待時(shí)間
int waitInterval; // 嘗試獲取連接時(shí)等待間隔時(shí)間
int maxConnectionCount; // 最大連接數(shù)
static QMutex mutex;
static QWaitCondition waitConnection;
static ConnectionPool *instance;
};
#endif // CONNECTIONPOOL
SqlDatabase是Qt框架中操作數(shù)據(jù)庫(kù)的重要類之一,可以通過(guò)該類實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接的建立和操作。ConnectionPool類是自定義的一個(gè)庫(kù)連接池類,在該類中定義了數(shù)據(jù)庫(kù)連接的各種屬性,并包含了多個(gè)數(shù)據(jù)庫(kù)連接對(duì)象。
在ConnectionPool類的實(shí)現(xiàn)文件中,我們將實(shí)現(xiàn)具體的方法。定義如下靜態(tài)變量:
QMutex ConnectionPool::mutex;
QWaitCondition ConnectionPool::waitConnection;
ConnectionPool* ConnectionPool::instance = nullptr;
用靜態(tài)變量ConnectionPool::instance來(lái)存儲(chǔ)ConnectionPool類的唯一實(shí)例,同時(shí)使用QMutex和QWaitCondition分別保護(hù)多線程的同步和條件變量的使用。
接著,實(shí)現(xiàn)單態(tài)模式中的getInstance()方法。該方法用于返回ConnectionPool類的唯一實(shí)例:
ConnectionPool& ConnectionPool::getInstance() {
if (nullptr == instance) {
QMutexLocker locker(&mutex);
if (nullptr == instance) {
instance = new ConnectionPool();
}
}
return *instance;
}
在getInstance ()方法中,使用互斥鎖QMutexLocker保證了多線程同步,使用雙重檢查鎖定機(jī)制確保了ConnectionPool類的唯一實(shí)例。
然后,我們可以實(shí)現(xiàn)如下連接的建立與關(guān)閉方法openConnection()和closeConnection()。openConnection()方法用于從連接池中獲取一個(gè)數(shù)據(jù)庫(kù)連接,closeConnection()方法用于還回這個(gè)數(shù)據(jù)庫(kù)連接:
QSqlDatabase ConnectionPool::openConnection() {
ConnectionPool& pool = ConnectionPool::getInstance();
QString connectionName;
QMutexLocker locker(&mutex);
// 已創(chuàng)建連接數(shù)
int connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
// 如果連接已經(jīng)用完,等待 waitInterval 毫秒看看是否有可用連接,最長(zhǎng)等待 maxWaitTime 毫秒
for (int i = 0;
i < pool.maxWaitTime
&& pool.unusedConnectionNames.size() == 0 && connectionCount == pool.maxConnectionCount;
i += pool.waitInterval) {
waitConnection.wait(&mutex, pool.waitInterval);
// 重新計(jì)算已創(chuàng)建連接數(shù)
connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
}
if (pool.unusedConnectionNames.size() > 0) {
// 有已經(jīng)回收的連接,復(fù)用它們
connectionName = pool.unusedConnectionNames.dequeue();
} else if (connectionCount < pool.maxConnectionCount) {
// 沒(méi)有已經(jīng)回收的連接,但是沒(méi)有達(dá)到最大連接數(shù),則創(chuàng)建新的連接
connectionName = QString("Connection-%1").arg(connectionCount + 1);
} else {
// 已經(jīng)達(dá)到最大連接數(shù)
qDebug() << "Cannot create more connections.";
return QSqlDatabase();
}
// 創(chuàng)建連接
QSqlDatabase db = pool.createConnection(connectionName);
// 有效的連接才放入 usedConnectionNames
if (db.isOpen()) {
pool.usedConnectionNames.enqueue(connectionName);
}
return db;
}
void ConnectionPool::closeConnection(QSqlDatabase connection) {
ConnectionPool& pool = ConnectionPool::getInstance();
QString connectionName = connection.connectionName();
// 如果是我們創(chuàng)建的連接,從 used 里刪除,放入 unused 里
if (pool.usedConnectionNames.contains(connectionName)) {
QMutexLocker locker(&mutex);
pool.usedConnectionNames.removeOne(connectionName);
pool.unusedConnectionNames.enqueue(connectionName);
waitConnection.wakeOne();
}
}
在openConnection()方法中,首先獲取互斥鎖QMutexLock,在連接池中查找是否有空閑連接。如果有,直接返回該連接,否則查看可用的連接數(shù)是否達(dá)到上限,如果沒(méi)有,則創(chuàng)建新的連接。當(dāng)新連接創(chuàng)建成功后,檢查該連接是否打開(kāi),如果打開(kāi),則更新連接數(shù)。最后釋放互斥鎖。
closeConnection()方法用于還回連接到連接池中,并檢查連接池中連接數(shù)量是否超過(guò)設(shè)定的更大值,如果超過(guò),則移除最早的連接。程序執(zhí)行完該方法后,也應(yīng)該釋放互斥鎖。
我們實(shí)現(xiàn)ConnectionPool的構(gòu)造函數(shù)和析構(gòu)函數(shù):
ConnectionPool::ConnectionPool() {
hostName = "127.0.0.1";//主機(jī)名
databaseName = "DRIVER={SQL SERVER};SERVER=127.0.0.1;DATABASE=testDB";//需要訪問(wèn)的數(shù)據(jù)庫(kù)
username = "sa"; //用戶名
password = "123456"; //密碼
databaseType = "QODBC"; //數(shù)據(jù)庫(kù)類型
testOnBorrow = true;
testOnBorrowSql = "SELECT 1";
maxWaitTime = 1000;
waitInterval = 200;
maxConnectionCount = 1000;
}
ConnectionPool::~ConnectionPool() {
// 銷毀連接池的時(shí)候刪除所有的連接
foreach(QString connectionName, usedConnectionNames) {
QSqlDatabase::removeDatabase(connectionName);
}
foreach(QString connectionName, unusedConnectionNames) {
QSqlDatabase::removeDatabase(connectionName);
}
}
void ConnectionPool::release() {
QMutexLocker locker(&mutex);
delete instance;
instance = nullptr;
}
QSqlDatabase ConnectionPool::createConnection(const QString &connectionName) {
// 連接已經(jīng)創(chuàng)建過(guò)了,復(fù)用它,而不是重新創(chuàng)建
if (QSqlDatabase::contains(connectionName)) {
QSqlDatabase db1 = QSqlDatabase::database(connectionName);
if (testOnBorrow) {
// 返回連接前訪問(wèn)數(shù)據(jù)庫(kù),如果連接斷開(kāi),重新建立連接
qDebug() << "Test connection on borrow, execute:" << testOnBorrowSql << ", for" << connectionName;
QSqlQuery query(testOnBorrowSql, db1);
if (query.lastError().type() != QSqlError::NoError && !db1.open()) {
qDebug() << "Open datatabase error:" << db1.lastError().text();
return QSqlDatabase();
}
}
return db1;
}
// 創(chuàng)建一個(gè)新的連接 注意:如果需要跨線程操作時(shí)這里需要連接時(shí)需要設(shè)置個(gè)靜態(tài)連接數(shù)據(jù)庫(kù)
QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, connectionName);
db.setHostName(hostName);
db.setDatabaseName(databaseName);
db.setUserName(username);
db.setPassword(password);
db.setPort(3306);
if (!db.open()) {
qDebug() << "Open datatabase error:" << db.lastError().text();
return QSqlDatabase();
}
return db;
}
在ConnectionPool類的構(gòu)造函數(shù)中,預(yù)先創(chuàng)建maxConnectionCount個(gè)數(shù)據(jù)庫(kù)連接,并存儲(chǔ)在usedConnectionNames中。在該構(gòu)造函數(shù)調(diào)用后,用戶可以直接通過(guò)openConnection()方法獲取連接,加快數(shù)據(jù)庫(kù)操作的速度。
數(shù)據(jù)庫(kù)連接長(zhǎng)時(shí)間不操作是可能會(huì)斷開(kāi),檢查數(shù)據(jù)庫(kù)的配置連接時(shí)間,一般會(huì)有時(shí)間限制,建議你程序啟動(dòng)需要和數(shù)據(jù)庫(kù)交互時(shí),先判斷數(shù)據(jù)庫(kù)是否是連接狀態(tài),未連接時(shí)重新連接。
關(guān)于qt中數(shù)據(jù)庫(kù)連接池的介紹到此就結(jié)束了。
如有侵權(quán),聯(lián)系刪除。