小優智能科技有限公司成立于2015年底,是一家專注于高精度3D機器視覺模組研發、生產及銷售的高科技企業。
公司自主研發的3D機器視覺模組采用激光/DLP白光編碼光柵結構光+雙工業相機方案,還原物體三維信息,廣泛應用于消費電子領域、工業領域和安防領域,具有精度高、速度快、成本低的優勢。
基于C++語言實現Qt框架下的數據庫連接池應用
隨著互聯網和大數據時代的到來,數據庫操作已經成為許多企業和應用程序不可或缺的重要部分。數據庫連接池技術的應用,可以提高數據庫使用效率,減少資源和時間的浪費。基于C++語言的Qt框架,也可以實現數據庫連接池技術,本文將介紹如何使用Qt實現數據庫連接池,讓你的數據庫操作更加高效。
一、什么是數據庫連接池
數據庫連接池是一種通過預先建立多個數據庫連接,在應用程序的運行過程中重復利用數據庫連接的技術。通俗的說,就是在應用程序中預先建立多個數據庫連接,當需要訪問數據庫時從連接池中獲取一個數據庫連接,用完后還回連接池中。這種利用池化技術的方式能夠降低新建連接的時間和資源消耗,提高數據庫的操作效率。下面,我們將使用Qt實現數據庫連接池。
二、Qt中數據庫連接的建立與使用
在Qt中,數據庫連接的建立與使用非常簡單。首先需要創建一個QSqlDatabase實例,在該實例中設置需要連接的數據庫類型、主機和用戶名等信息。然后,使用QSqlDatabase::open()函數打開該數據庫連接,并進行一些操作。
#include <QSqlDatabase >
#include <QSqlQuery>
#include <QDebug>
QSqlDatabase db = QSqlDatabase::addDatabase(“QMYSQL”);
db.setHostName(“localhost”);
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數據庫的過程。首先使用QSqlDatabase::addDatabase(“QMYSQL”)函數創建一個QSqlDatabase實例,在該實例中設置需要連接的數據庫類型、主機和用戶名等信息。然后,使用QSqlDatabase::open()函數打開該數據庫連接。在數據庫連接成功后,使用QSqlQuery實例進行一些操作。注意,一定要在使用完QSqlQuery對象后,調用其~QSqlQuery()函數釋放資源。
三、Qt實現數據庫連接池
下面,我們將基于Qt實現一個簡單的數據庫連接池。創建一個ConnectionPool類,該類中包含了多個數據庫連接對象。ConnectionPool的.h文件內容如下:
#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(); // 關閉所有的數據庫連接
static QSqlDatabase openConnection(); // 獲取數據庫連接
static void closeConnection(QSqlDatabase connection); // 釋放數據庫連接回連接池
~ConnectionPool();
private:
static ConnectionPool& getInstance();
ConnectionPool();
ConnectionPool(const ConnectionPool &other);
ConnectionPool& operator=(const ConnectionPool &other);
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 ConnectionPool *instance;
};
#endif // CONNECTIONPOOL
SqlDatabase是Qt框架中操作數據庫的重要類之一,可以通過該類實現數據庫連接的建立和操作。ConnectionPool類是自定義的一個庫連接池類,在該類中定義了數據庫連接的各種屬性,并包含了多個數據庫連接對象。
在ConnectionPool類的實現文件中,我們將實現具體的方法。定義如下靜態變量:
QMutex ConnectionPool::mutex;
QWaitCondition ConnectionPool::waitConnection;
ConnectionPool* ConnectionPool::instance = nullptr;
用靜態變量ConnectionPool::instance來存儲ConnectionPool類的唯一實例,同時使用QMutex和QWaitCondition分別保護多線程的同步和條件變量的使用。
接著,實現單態模式中的getInstance()方法。該方法用于返回ConnectionPool類的唯一實例:
ConnectionPool& ConnectionPool::getInstance() {
if (nullptr == instance) {
QMutexLocker locker(&mutex);
if (nullptr == instance) {
instance = new ConnectionPool();
}
}
return *instance;
}
在getInstance ()方法中,使用互斥鎖QMutexLocker保證了多線程同步,使用雙重檢查鎖定機制確保了ConnectionPool類的唯一實例。
然后,我們可以實現如下連接的建立與關閉方法openConnection()和closeConnection()。openConnection()方法用于從連接池中獲取一個數據庫連接,closeConnection()方法用于還回這個數據庫連接:
QSqlDatabase ConnectionPool::openConnection() {
ConnectionPool& pool = ConnectionPool::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 ConnectionPool::closeConnection(QSqlDatabase connection) {
ConnectionPool& pool = ConnectionPool::getInstance();
QString connectionName = connection.connectionName();
// 如果是我們創建的連接,從 used 里刪除,放入 unused 里
if (pool.usedConnectionNames.contains(connectionName)) {
QMutexLocker locker(&mutex);
pool.usedConnectionNames.removeOne(connectionName);
pool.unusedConnectionNames.enqueue(connectionName);
waitConnection.wakeOne();
}
}
在openConnection()方法中,首先獲取互斥鎖QMutexLock,在連接池中查找是否有空閑連接。如果有,直接返回該連接,否則查看可用的連接數是否達到上限,如果沒有,則創建新的連接。當新連接創建成功后,檢查該連接是否打開,如果打開,則更新連接數。最后釋放互斥鎖。
closeConnection()方法用于還回連接到連接池中,并檢查連接池中連接數量是否超過設定的更大值,如果超過,則移除最早的連接。程序執行完該方法后,也應該釋放互斥鎖。
我們實現ConnectionPool的構造函數和析構函數:
ConnectionPool::ConnectionPool() {
hostName = "127.0.0.1";//主機名
databaseName = "DRIVER={SQL SERVER};SERVER=127.0.0.1;DATABASE=testDB";//需要訪問的數據庫
username = "sa"; //用戶名
password = "123456"; //密碼
databaseType = "QODBC"; //數據庫類型
testOnBorrow = true;
testOnBorrowSql = "SELECT 1";
maxWaitTime = 1000;
waitInterval = 200;
maxConnectionCount = 1000;
}
ConnectionPool::~ConnectionPool() {
// 銷毀連接池的時候刪除所有的連接
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) {
// 連接已經創建過了,復用它,而不是重新創建
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 datatabase error:" << db1.lastError().text();
return QSqlDatabase();
}
}
return db1;
}
// 創建一個新的連接 注意:如果需要跨線程操作時這里需要連接時需要設置個靜態連接數據庫
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類的構造函數中,預先創建maxConnectionCount個數據庫連接,并存儲在usedConnectionNames中。在該構造函數調用后,用戶可以直接通過openConnection()方法獲取連接,加快數據庫操作的速度。
數據庫連接長時間不操作是可能會斷開,檢查數據庫的配置連接時間,一般會有時間限制,建議你程序啟動需要和數據庫交互時,先判斷數據庫是否是連接狀態,未連接時重新連接。
關于qt中數據庫連接池的介紹到此就結束了。
如有侵權,聯系刪除。