三十四、Qt之创建数据库连接池

数据库连接属于大消耗资源,若频繁的删除再创建,这样会极大地降低程序的响应性能。所以这里需要引入数据库连接池,将不用的连接放回池中,需要用的时候,再将资源拿回来,重新复用。案例如下:

ConnectionPool.h

#ifndef CONNECTIONPOOL_H
#define CONNECTIONPOOL_H

#include <QObject>
#include <QtSql/QtSql>
#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>

class ConnectionPool
{
public:
    
    ~ConnectionPool();

    static void release(); //关闭连接池

    static QSqlDatabase openConnection(); //获取数据库连接

    static void closeConnection(QSqlDatabase connection); //释放数据库连接回连接池

private:
    //数据库信息
    QString hostName;
    QString databaseName;
    QString username;
    QString password;
    QString databaseType;

    bool testOnBorrow; //取得连接的时候验证连接是否有效
    QString testOnBorrowSql; //测试访问数据库的 SQL

    int maxWaitTime; //获取连接最大等待时间
    int waitInterval; //尝试获取连接时等待间隔时间
    int maxConnectionCount; //最大连接数
    
    QQueue<QString> usedConnectionNames; //已使用的数据库连接名
    QQueue<QString> unUsedConnectionNames; //未使用的数据库连接名

    static QMutex mutex; //同步锁
    static QWaitCondition waitCondition; //条件等待锁
    static ConnectionPool *instance = NULL;
    
    ConnectionPool();
    
    //单例,需提供拷贝构造函数和赋值运算符
    static ConnectionPool& getInstance();
    ConnectionPool(const ConnectionPool &other); //拷贝构造函数
    ConnectionPool& operator=(const ConnectionPool &other); //赋值运算符
    
    QSqlDatabase createConnection(const QString &connectionName);
    

};

#endif // CONNECTIONPOOL_H

ConnectionPool.cpp

#include "connectionpool.h"

ConnectionPool::ConnectionPool()
{
    // 创建数据库连接的这些信息在实际开发的时都需要通过读取配置文件得到,
    // 这里为了演示方便所以写死在了代码里。
    hostName     = "127.0.0.1";
    databaseName = "qq";
    username     = "root";
    password     = "root";
    databaseType = "QMYSQL";
    testOnBorrow = true;
    testOnBorrowSql = "SELECT 1";

    maxWaitTime  = 1000;
    waitInterval = 200;
    maxConnectionCount  = 100;
}

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 = NULL;
}

QSqlDatabase ConnectionPool::openConnection()
{
    ConnectionPool &pool = ConnectionPool::getInstance();
    QString connectionName;
    mutex.lock();
    // 已创建连接数
    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 += waitInterval) {
        //阻塞,期间其他线程可以使用 mutex 锁,等待唤醒,唤醒后继续加锁,执行后续代码
        waitCondition.wait(&mutex, pool.waitInterval);
        // 重新计算已创建连接数
        connectionCount = pool.unUsedConnectionNames.size() + pool.usedConnectionNames.size();
    }

    if (pool.unUsedConnectionNames.size() > 0)
        // 有已经回收的连接,复用它们
        connectionName = pool.unUsedConnectionNames.dequeue();
    else if (connectionCount < maxConnectionCount)
        // 没有已经回收的连接,但是没有达到最大连接数,则创建新的连接
        connectionName = QString("Connection-%1").arg(connectionCount + 1);
    else {
        // 已经达到最大连接数
        qDebug() << "Cannot create more connections.";
        //这里相当于 return NULL
        return QSqlDatabase();
    }
	mutex.unlock();
	//因为创建连接很耗时,所以不放在 lock 的范围内,提高并发效率
    //获取新的连接(为什么不是创建,因为有可能是已经回收的连接,直接复用即可)
    QSqlDatabase db = pool.createConnection(connectionName);

    // 有效的连接才放入 usedConnectionNames
    if (db.isOpen()){
    	mutex.lock();
    	pool.usedConnectionNames.enqueue(connectionName);
    	mutex.unlock();
	}
    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);
        //唤醒所有线程,此时阻塞的 QWaitCondition 会被唤醒
        waitCondition.wakeOne();
    }
}

/**
 * 单例:双重检查锁
 */
ConnectionPool &ConnectionPool::getInstance()
{
    if (NULL = instance) {
        QMutexLocker locker(&mutex);
        if (NULL = instance) {
            instance = new ConnectionPool();
        }
    }
    return *instance;
}

QSqlDatabase ConnectionPool::createConnection(const QString &connectionName)
{
    // 连接已经创建过了,复用它,而不是重新创建
    if (QSqlDatabase::contains(connectionName)) {
        QSqlDatabase unUsedDb = QSqlDatabase::database(connectionName);

        if (testOnBorrow) {
            // 返回连接前访问数据库,如果连接断开,重新建立连接
            qDebug() << "Test connection on borrow, execute:" << testOnBorrowSql << ", for" << connectionName;
            //测试连接
            QSqlQuery query(testOnBorrowSql, unUsedDb);
            // 若查询报错,或者连接无法打开
            if (query.lastError().type() != QSqlError::NoError && !unUsedDb.open()) {
                qDebug() << "Open datatabase error:" << query.lastError().text();
                return QSqlDatabase();
            }
        }
        return unUsedDb;
    }

    //新创建连接
    QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, connectionName);
    db.setHostName(hostName);
    db.setDatabaseName(databaseName);
    db.setUserName(username);
    db.setPassword(password);

    if (!db.open()) {
        qDebug() << "Open datatabase error:" << db.lastError().text();
        return QSqlDatabase();
    }
    return db;
}



  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值