Qt 线程连接池——自定义线程连接池(模块化操作)

注:本博客为转载,原博客地址:https://blog.csdn.net/lwwl12/article/details/76124210,本可以意在学习记录

1、使用配置文件,读取数据库类型、配置等。

2、使用了 QMutex,QWaitCondition 和 QMutexLocker 来保护共享资源,支持多线程。

3、优化连接,严格控制连接。

(1)如果没有可复用连接 pool.unusedConnectionNames.size() == 0 且已经创建的连接数达到最大,则等待,等待期间有连接被释放回连接池就复用这个连接,如果超时都没有可用连接,则返回一个无效的连接 QSqlDatabase()

(2)如果没有可复用连接,但是已经创建的连接数没有达到最大,那么就创建一个新的连接,并把这个连接的名字添加到 usedConnectionNames

(3)如果有可复用的连接,则复用它,把它的名字从 unusedConnectionNames 里删除并且添加到 usedConnectionNames

sqlconnectionpool.h

#ifndef SQLCONNECTIONPOOL_H
#define SQLCONNECTIONPOOL_H
/*
 *为了支持多线程,使用了 QMutex,QWaitCondition 和 QMutexLocker 来保护共享资源
 * usedConnectionNames 和 unusedConnectionNames 的读写。
 *
*/
#include <QSqlDatabase>
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>

class SqlConnectionPool
{
public:
    static bool isDirOrFileExits(QString dbName);
    static bool createSqlitePath(QString path);
    static void release();//关闭所有数据库连接
    static QSqlDatabase openConnection();//获取数据库连接
    static void closeConnection(QSqlDatabase connection);//释放连接回连接池
                                                         // 一般在 main() 函数返回前调用
    ~SqlConnectionPool();
private:
    static SqlConnectionPool& getInstance();//保证在程序运行的时候只有一个对象被创建,getInstance() 用于取得这个唯一的对象。
    SqlConnectionPool();
    SqlConnectionPool(const SqlConnectionPool &other);
    SqlConnectionPool& operator=(const SqlConnectionPool &other);
    QSqlDatabase createConnection(const QString &connectionName); // 创建数据库连接

    QQueue<QString> usedConnectionNames;   // 已使用的数据库连接名
    QQueue<QString> unusedConnectionNames; // 未使用的数据库连接名

    // 数据库信息
    QString hostName;
    QString databasePath;
    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 SqlConnectionPool *instance;


};

#endif // SQLCONNECTIONPOOL_H

sqlconnectionpool.cpp

#include "sqlconnectionpool.h"

#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
//配置文件头文件
#include <QSettings>
#include "system_config.h"
//----------初始化静态变量----------//
QMutex SqlConnectionPool::mutex;
QWaitCondition SqlConnectionPool::waitConnection;
SqlConnectionPool* SqlConnectionPool::instance;
SqlConnectionPool::SqlConnectionPool()
{
    // 创建数据库连接的这些信息在实际开发的时都需要通过读取配置文件得到
    QSettings set( QString::fromUtf8(DIR_WORKSPACE)+\
                   QString::fromUtf8(DIR_CONFIG)+\
                   QString::fromUtf8(FILE_CONFIG) , QSettings::IniFormat);//文件存在则打开,不存在则创建
    set.setIniCodec("UTF-8");
        set.beginGroup("SQL-Standard");
        hostName = set.value("hostName").toString();
        databaseName = set.value("databaseName").toString();
        databasePath = set.value("databasePath").toString();
        username = set.value("username").toString();
        password = set.value("password").toString();
        databaseType = set.value("databaseType").toString();
        testOnBorrow = set.value("testOnBorrow").toBool();
        testOnBorrowSql = set.value("testOnBorrowSql").toString();
        maxWaitTime = set.value("maxWaitTime").toInt();
        waitInterval = set.value("waitInterval").toInt();
        maxConnectionCount = set.value("maxConnectionCount").toInt();
        set.endGroup();

}

QSqlDatabase SqlConnectionPool::createConnection(const QString &connectionName)
{
    //当连接已经创建过,复用他
    if(QSqlDatabase::contains(connectionName))
    {
        QSqlDatabase db = QSqlDatabase::database(connectionName);
        if(testOnBorrow)
        {
            //返回连接前访问数据库,如果连接断开,重新建立连接
            qDebug() << "Test connection on borrow, execute:" << testOnBorrowSql << ", for" << connectionName;
            QSqlQuery query(testOnBorrowSql,db);
            if(query.lastError().type() != QSqlError::NoError && !db.open())
            {
                qDebug() << "Open datatabase error:" << db.lastError().text();
                return QSqlDatabase();
            }
        }
        return db;
    }
    // 创建一个新的连接,首先需要判断数据库路径是否存在

    //①判断数据库路径是否存在
    if( !isDirOrFileExits(databasePath) )
    {
        qDebug()<<databasePath<<"路径不存在!创建";
        createSqlitePath(databasePath);
    }
    //②初始化数据库连接
    QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, connectionName);
    db.setHostName(hostName);
    //③获取数据库名,打开数据库
    db.setDatabaseName(databaseName);
    if(username != NULL && password != NULL)
    {
        db.setUserName(username);
        db.setPassword(password);
    }
    if (!db.open()) {
        qDebug() << "Open datatabase error:" << db.lastError().text();
        return QSqlDatabase();
    }

    return db;
}
bool SqlConnectionPool::isDirOrFileExits(QString dbName)
{
    QFileInfo fileinfo(dbName);
    return fileinfo.exists();
}

bool SqlConnectionPool::createSqlitePath(QString path)
{
    QDir parameterDir;
    return parameterDir.mkpath(path);
}

void SqlConnectionPool::release()
{
    QMutexLocker locker(&mutex);
    delete instance;
    instance = NULL;
}

QSqlDatabase SqlConnectionPool::openConnection()
{
    SqlConnectionPool& pool = SqlConnectionPool::getInstance();//变量初始化
    QString connectionName;

    QMutexLocker locker(&mutex);
    //已经创建的连接数
    int connectionCount = pool.unusedConnectionNames.size()+pool.usedConnectionNames.size();
    //如果连接已经用完,等待waitInterval毫秒看看是否有可用的连接,最长等待maxWaitTime毫秒
/*
如果没有可复用连接 pool.unusedConnectionNames.size() == 0 且已经创建的连接数达到最大,则等待,
等待期间有连接被释放回连接池就复用这个连接,如果超时都没有可用连接,则返回一个无效的连接 QSqlDatabase()。
*/

    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();
        //qDebug()<<"no connection to use,wait ...";
    }
    if(pool.unusedConnectionNames.size()>0)
    {
        //有已经回收的连接,复用他们
        connectionName = pool.unusedConnectionNames.dequeue();//出队
        //qDebug()<<"have old connections,use them";
    }else if( connectionCount < pool.maxConnectionCount )
    {
        //还有空余,创建新的连接
        connectionName = QString("Connection-%1").arg(connectionCount + 1);
    }else
    {
        //已经达到最大连接数
        qDebug()<<"Maximum number of connections reached!!!";
        return QSqlDatabase();
    }

    //创建连接
    QSqlDatabase db = pool.createConnection(connectionName);
    //有效的连接才放入 usedConnectionNames
    if(db.isOpen())
    {
        pool.usedConnectionNames.enqueue(connectionName);
    }
    return db;
}

void SqlConnectionPool::closeConnection(QSqlDatabase connection)
{
    SqlConnectionPool& pool = SqlConnectionPool::getInstance();//变量初始化
    QString connectionName = connection.connectionName();//获取数据库连接名儿
    if(pool.usedConnectionNames.contains(connectionName))//如果是我们创建的,放入unused
    {
        QMutexLocker locker(&mutex);
        pool.usedConnectionNames.removeOne(connectionName);
        pool.unusedConnectionNames.enqueue(connectionName);
        waitConnection.wakeOne();//唤醒一个等待的线程
    }
}

SqlConnectionPool::~SqlConnectionPool()
{
    // 销毁连接池的时候删除所有的连接
    foreach(QString connectionName, usedConnectionNames) {//循环遍历
        QSqlDatabase::removeDatabase(connectionName);
    }

    foreach(QString connectionName, unusedConnectionNames) {
        QSqlDatabase::removeDatabase(connectionName);
    }
}

SqlConnectionPool &SqlConnectionPool::getInstance()
{
    if(NULL == instance)
    {
        QMutexLocker locker(&mutex);
        if(NULL == instance)
        {
            instance = new SqlConnectionPool();//sqlconnectionpool静态变量初始化
        }
    }
    return *instance;
}

猜你喜欢

转载自www.cnblogs.com/shuoguoleilei/p/11425743.html
今日推荐