Qt 数据库连接池

参考博客

https://qtdebug.com/qtbook-db-connection-pool/

* 数据库连接池特点:
    * 获取连接时不需要了解连接的名字,连接池内部维护连接的名字
    * 支持多线程,保证获取到的连接一定是没有被其他线程正在使用
    * 按需创建连接,可以创建多个连接,可以控制连接的数量
    * 连接被复用,不是每次都重新创建一个新的连接(连接的创建是一个很消耗资源的过程)
    * 连接断开了后会自动重连
    * 当无可用连接时,获取连接的线程会等待一定时间尝试继续获取,直到取到有效连接或者超时返回一个无效的连接
    * 关闭连接很简单

核心代码

QPair<bool, QSqlDatabase> ZLDBConnPool::openConnection(ZLDBConnPool::DbType emdbtype, const QString &connectionName)
{
    // 1. 创建连接的全名: 基于线程的地址和传入进来的 connectionName,因为同一个线程可能申请创建多个数据库连接
    // 2. 如果连接已经存在,复用它,而不是重新创建
    //    2.1 返回连接前访问数据库,如果连接断开,可以重新建立连接 (测试: 关闭数据库几分钟后再启动,再次访问数据库)
    // 3. 如果连接不存在,则创建连接
    // 4. 线程结束时,释放在此线程中创建的数据库连接

    // [1] 创建连接的全名: 基于线程的地址和传入进来的 connectionName,因为同一个线程可能申请创建多个数据库连接
    QString baseConnectionName = "conn_" + QString::number(quint64(QThread::currentThread()), 16);
    QString fullConnectionName = baseConnectionName +"_"+ connectionName;
//    qDebug() << fullConnectionName;
    if (QSqlDatabase::contains(fullConnectionName)) {
        // [2] 如果连接已经存在,复用它,而不是重新创建
        QSqlDatabase existingDb = QSqlDatabase::database(fullConnectionName);

        // [2.1] 返回连接前访问数据库,如果连接断开,可以重新建立连接 (测试: 关闭数据库几分钟后再启动,再次访问数据库)
        QSqlQuery query("SELECT 1", existingDb);

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

        return {true,existingDb};
    } else {
        // [3] 如果连接不存在,则创建连接
        if (qApp != nullptr) {
            // [4] 线程结束时,释放在此线程中创建的数据库连接
            QObject::connect(QThread::currentThread(), &QThread::finished, qApp, [fullConnectionName] {
                if (QSqlDatabase::contains(fullConnectionName)) {
                    QSqlDatabase::removeDatabase(fullConnectionName);
                    qDebug().noquote() << QString("Connection deleted: %1").arg(fullConnectionName);
                }
            });
        }

        return createConnection(emdbtype,fullConnectionName);
    }
}

多线程下测试:

可以看到每个线程都用了一次数据库连接,而且使用线程执行完了会自动销毁连接

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a62610_sqlite, sn: 1

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_9f3cc0_sqlite, sn: 2

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a438a0_sqlite, sn: 3

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a43680_sqlite, sn: 4

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a43800_sqlite, sn: 5

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a44060_sqlite, sn: 6

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a43bc0_sqlite, sn: 7

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a44700_sqlite, sn: 8

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_a42ce0_sqlite, sn: 9

"1234"

支持数据库的驱动: ("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

Connection created: conn_69656b0_sqlite, sn: 10

"1234"

Connection deleted: conn_a62610_sqlite

Connection deleted: conn_9f3cc0_sqlite

Connection deleted: conn_a438a0_sqlite

Connection deleted: conn_a43680_sqlite

Connection deleted: conn_a43800_sqlite

Connection deleted: conn_a44060_sqlite

Connection deleted: conn_a43bc0_sqlite

Connection deleted: conn_a44700_sqlite

Connection deleted: conn_a42ce0_sqlite

Connection deleted: conn_69656b0_sqlite