QT实现sqlite数据库连接池

#ifndef CONNECTIONPOOL_H
#define CONNECTIONPOOL_H
/*****************************************************************************
**FileName: 数据库连接池
**Function: 获取连接时不需要了解连接的名字
支持多线程,保证获取到的连接一定是没有被其他线程正在使用
按需创建连接
可以创建多个连接
可以控制连接的数量
连接被复用,不是每次都重新创建一个新的连接
连接断开了后会自动重连
当无可用连接时,获取连接的线程会等待一定时间尝试继续获取,直到超时才会返回一个无效的连接

**Version record:
**Version       Author         Data          Description
**v1.0.1        wlj   2019.05.16        first draft
*******************************************************************************/

#include<QtSql>
#include<QQueue>
#include<QString>
#include<QMutex>
#include<QMutexLocker>

class ConnectionPool
{
public:
    ~ConnectionPool();
    /******************************************************************************
* Function: 参数设置
* InPut   : databaseName数据库名称 databaseType数据库类型 hostName端口号
* OutPut  :无
* Return  :无
* Other   :
* Author  : wlj   2019.05.22
*******************************************************************************/
    static void SetParam(QString databaseName,QString databaseType,QString hostName = "");
    /******************************************************************************
* Function:   从连接池里获取连接
* InPut   : 无
* OutPut  : 无
* Return  : 返回获取到的链接db
* Other   : OpenConnection-CloseConnection要成对使用
* Author  : wlj   2019.05.22
*******************************************************************************/
    static QSqlDatabase OpenConnection();//用于从连接池里获取连接
    /******************************************************************************
* Function:   连接释放回连接池
* InPut   : connection数据库链接
* OutPut  : 无
* Return  : 无
* Other   : 并不会真正的关闭连接,而是把连接放回连接池复用;OpenConnection-CloseConnection要成对使用
* Author  : wlj   2019.05.22
*******************************************************************************/
    static void CloseConnection(QSqlDatabase connection);//并不会真正的关闭连接,而是把连接放回连接池复用。

private:
    ConnectionPool();
    static ConnectionPool& GetInstance();
    static void Release();//真正的关闭所有的连接
    ConnectionPool(const ConnectionPool &){}
    ConnectionPool& operator=(const ConnectionPool &);
    QSqlDatabase CreateConnection(const QString &connectionName); // 创建数据库连接
    class GarbageCollector
    {
    public:
        ~GarbageCollector()
        {
            ConnectionPool::Release();
        }
    };

private:
    static GarbageCollector m_gc;          //静态变量,只是为了释放单例

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

    // 数据库信息
    QString m_HostName;
    QString m_DatabaseName;// 如果是 SQLite 则为数据库文件名
    //    QString m_Username;// 如果是 SQLite 不需要
    //    QString m_Password;// 如果是 SQLite 不需要
    QString m_DatabaseType;

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

    int m_MaxWaitTime;  // 获取连接最大等待时间
    int m_WaitInterval; // 尝试获取连接时等待间隔时间
    int m_MaxConnectionCount; // 最大连接数

    static QMutex m_Mutex;
    static QWaitCondition m_WaitConnection;
    static ConnectionPool *m_Instance;
};

#endif // CONNECTIONPOOL_H
#include "connectionpool.h"
#include <QDebug>

#define HOSTNAME "127.0.0.1"
#define DATABASENAME "ChatMsg/ChatMsg.db"
#define DATABASETYPE "QSQLITE"
#define TESTONBORROWSQL "SELECT 1"
#define MAXWAITTIME 1000
#define WAITINTERVAL 200
#define MAXCONNECTIONCOUNT 50

QMutex ConnectionPool::m_Mutex;
QWaitCondition ConnectionPool::m_WaitConnection;
ConnectionPool::GarbageCollector ConnectionPool::m_gc;
ConnectionPool* ConnectionPool::m_Instance = NULL;

ConnectionPool::ConnectionPool()
{
    qDebug()<<"ConnectionPool::ConnectionPool";
    m_HostName = HOSTNAME;
    m_DatabaseName =  QCoreApplication::applicationDirPath() + "//" + DATABASENAME;
    m_DatabaseType = DATABASETYPE;
    m_TestOnBorrow = true;
    m_TestOnBorrowSql = TESTONBORROWSQL;

    m_MaxWaitTime = MAXWAITTIME;
    m_WaitInterval = WAITINTERVAL;
    m_MaxConnectionCount = MAXCONNECTIONCOUNT;
}
ConnectionPool::~ConnectionPool()
{
    foreach(QString connectionName, m_UsedConnectionNames)
    {
        QSqlDatabase::removeDatabase(connectionName);
    }

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

ConnectionPool & ConnectionPool::GetInstance()
{
    if(NULL == m_Instance)
    {
        QMutexLocker locker(&m_Mutex);
        if(NULL == m_Instance)
        {
            m_Instance = new ConnectionPool();
        }
    }
    return *m_Instance;
}

void ConnectionPool::Release()
{
    QMutexLocker locker(&m_Mutex);
    delete m_Instance;
    m_Instance = NULL;
    qDebug()<<"release ConnectionPool::m_Instance";
}
void ConnectionPool::SetParam(QString databaseName,QString databaseType,QString hostName)
{
    ConnectionPool &pool = ConnectionPool::GetInstance();
    pool.m_HostName = hostName;
    pool.m_DatabaseName = databaseName;
    pool.m_DatabaseType = databaseType;
}
QSqlDatabase ConnectionPool::OpenConnection()
{
    ConnectionPool &pool = ConnectionPool::GetInstance();
    QString connectionName;

    QMutexLocker locker(&m_Mutex);

    // 已创建连接数
    int connectionCount = pool.m_UnusedConnectionNames.size() + pool.m_UsedConnectionNames.size();

    // 如果连接已经用完,等待 waitInterval 毫秒看看是否有可用连接,最长等待 maxWaitTime 毫秒
    for(int i = 0; (i < pool.m_MaxWaitTime)
        && (pool.m_UnusedConnectionNames.size() == 0)
        && (connectionCount == pool.m_MaxConnectionCount);
        i += pool.m_WaitInterval)
    {
        m_WaitConnection.wait(&m_Mutex, pool.m_WaitInterval);

        // 重新计算已创建连接数
        connectionCount = pool.m_UnusedConnectionNames.size() + pool.m_UsedConnectionNames.size();
    }
    if(0 < pool.m_UnusedConnectionNames.size() )
    {
        // 有已经回收的连接,复用它们
        connectionName = pool.m_UnusedConnectionNames.dequeue();
    }
    else if(connectionCount < pool.m_MaxConnectionCount)
    {
        // 没有已经回收的连接,但是没有达到最大连接数,则创建新的连接
        connectionName = QString("UserConnection - %1").arg(connectionCount + 1);
    }
    else
    {
        qWarning() << "connectionCount = "<<connectionCount<<"Cannot create more connections.";
        return QSqlDatabase();
    }

    // 创建连接
    QSqlDatabase db = pool.CreateConnection(connectionName);

    // 有效的连接才放入 usedConnectionNames
    if(db.isOpen())
    {
        pool.m_UsedConnectionNames.enqueue(connectionName);
    }
    else
    {
        // 连接无效
        qWarning() << "connections is open fail.";
        return QSqlDatabase();
    }
//qDebug()<<connectionName;
    return db;
}

void ConnectionPool::CloseConnection(QSqlDatabase connection)
{
    ConnectionPool &pool = ConnectionPool::GetInstance();
    QString connectionName = connection.connectionName();

    // 如果是我们创建的连接,从 used 里删除,放入 unused 里
    if(pool.m_UsedConnectionNames.contains(connectionName))
    {
        QMutexLocker locker(&m_Mutex);
        pool.m_UsedConnectionNames.removeOne(connectionName);
        pool.m_UnusedConnectionNames.enqueue(connectionName);
        m_WaitConnection.wakeOne();
    }
}

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

        if(!db1.isOpen())
        {
            // 返回连接前访问数据库,如果连接断开,重新建立连接
//                        qDebug() << "Reconnection: " << m_TestOnBorrowSql << " for " << connectionName;
            QSqlQuery query(m_TestOnBorrowSql, db1);

            if(!db1.open() && QSqlError::NoError != query.lastError().type() )
            {
                qDebug() << "Open datatabase error:" << db1.lastError().text();
                return QSqlDatabase();
            }
        }

        return db1;
    }

    // 创建一个新的连接
    QSqlDatabase db = QSqlDatabase::addDatabase(m_DatabaseType, connectionName);
    //    db.setHostName(m_HostName);
    db.setDatabaseName(m_DatabaseName);

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

    return db;
}

转自:https://www.cnblogs.com/qk2015/p/4832534.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值