原理

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。

而其实质上连接池就是通过单例,在进行工具类调用连接后通过一个集合来建立几个连接对象。在进行连接的时候来通过集合分配对象,采用对象锁来限制多线程的资源抢夺情况。

一、头文件

#ifndef CSQLDATABASE_H
#define CSQLDATABASE_H

#include <QtSql>
#include <QString>
#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>
#include <QQueue>
/*
 * qt时间样式
 * 年月:yyyy-mm-dd
 * 时间:hh24:mi:ss
 * 年月时间:yyyy-mm-dd hh24:mi:ss
 */


class CSQLDatabase
{
public:
    /*
     * 单例模式获取
     * 参数:ip,用户名,用户密码,数据库名,数据库类型,端口号,最大连接数,最长等待时间,等待间隔
     */
    static CSQLDatabase& GetInstance(QString qsHostName, QString qsUser, QString qsPassWd,
                                     QString qsDatabase, QString qsDatabaseType, int nPort,
                                     int nMaxConn, int nMaxWaitTime, int nWaitInter);

    bool                ExecuteStmt(QString qsSql); //用于增删改
    QSqlQuery           ExecuteQuery(QString qsSql);//用于查询

    ~CSQLDatabase();

private:
    CSQLDatabase(QString qsHostName, QString qsUser, QString qsPassWd,
                 QString qsDatabase, QString qsDatabaseType, int nPort,
                 int nMaxConn, int nMaxWaitTime, int nWaitInter);

    QSqlDatabase    OpenConnection();
    void            CloseConnection(QSqlDatabase connection);
    QSqlDatabase    CreateConnect(const QString& qsConnName);

private:
    int m_nPort;
    int m_nMaxConn;
    int m_nMaxWaitTime; //最长等待时间
    int m_nWaitInter;   //等待间隔

    QString m_qsHostName;
    QString m_qsUser;
    QString m_qsPassWd;
    QString m_qsDatabase;
    QString m_qsDatabaseType;


    QQueue<QString> m_queUseConnNames;
    QQueue<QString> m_queUnuserConnNames;

    static QMutex m_Mutex;
    static QWaitCondition m_WaitConn;
    static CSQLDatabase* m_pInstance;
};

static CSQLDatabase glSqlDatabse = CSQLDatabase::GetInstance("127.0.0.1", "aaron", "0", "db", "QOCI", 1521, 5, 10, 1);

#endif // CSQLDATABASE_H

二、源文件

#include "CSQLDatabase.h"

QMutex CSQLDatabase::m_Mutex;
CSQLDatabase* CSQLDatabase::m_pInstance = nullptr;
QWaitCondition CSQLDatabase::m_WaitConn;

CSQLDatabase& CSQLDatabase::GetInstance(QString qsHostName, QString qsUser, QString qsPassWd,
                                 QString qsDatabase, QString qsDatabaseType, int nPort,
                                 int nMaxConn, int nMaxWaitTime, int nWaitInter)
{
    if(m_pInstance == nullptr)
    {
        QMutexLocker locker(&m_Mutex);
        m_pInstance = new CSQLDatabase(qsHostName, qsUser, qsPassWd, qsDatabase,
                                       qsDatabaseType, nPort, nMaxConn, nMaxWaitTime, nWaitInter);
    }

    return *m_pInstance;
}

CSQLDatabase::CSQLDatabase(QString qsHostName, QString qsUser, QString qsPassWd,
                           QString qsDatabase, QString qsDatabaseType, int nPort,
                           int nMaxConn, int nMaxWaitTime, int nWaitInter)
    :m_qsHostName(qsHostName), m_qsUser(qsUser), m_qsPassWd(qsPassWd), m_qsDatabase(qsDatabase),
      m_qsDatabaseType(qsDatabaseType), m_nPort(nPort), m_nMaxConn(nMaxConn),
      m_nMaxWaitTime(nMaxWaitTime), m_nWaitInter(nWaitInter)
{
}

CSQLDatabase::~CSQLDatabase()
{
    QMutexLocker locker(&m_Mutex);
    foreach(QString connName, m_pInstance->m_queUseConnNames)   //移除所有使用中的连接
        QSqlDatabase::removeDatabase(connName);

    foreach(QString unConnName, m_pInstance->m_queUnuserConnNames)  //移除所有未使用的连接
        QSqlDatabase::removeDatabase(unConnName);

    m_pInstance = nullptr;
}

QSqlDatabase CSQLDatabase::OpenConnection()
{
    QMutexLocker locker(&m_Mutex);
    QString qsConnName;

    int nCountT = m_queUseConnNames.size() + m_queUnuserConnNames.size();
    //如果未到等待时间,并且所有的连接都在使用中,根据等待间隔进入到等待
    for(int i = 0; i < m_nMaxWaitTime && m_queUnuserConnNames.size() == 0 && nCountT == m_queUseConnNames.size(); i += m_nWaitInter)
    {
        //进行等待
        m_WaitConn.wait(&m_Mutex, m_nWaitInter);
        //重新计数总连接
        nCountT = m_queUseConnNames.size() + m_queUnuserConnNames.size();
    }

    //判断是否需要创建新连接,使用未使用连接,或者在连接全在使用时返回空的连接(可自己修改)
    if(m_queUnuserConnNames.size() > 0)
        qsConnName = m_queUnuserConnNames.dequeue();
    else if(nCountT < m_nMaxConn)
        qsConnName = QString("Connection-%1").arg(nCountT + 1);
    else {
        qDebug() << "All use sql connect";
        return QSqlDatabase();
    }

    //创建新连接,并放入到正在使用的连接容器中
    QSqlDatabase db = CreateConnect(qsConnName);
    if(db.isOpen())
        m_queUseConnNames.enqueue(qsConnName);

    return db;
}

void CSQLDatabase::CloseConnection(QSqlDatabase connection)
{
    //关闭连接,并将连接名放回未连接容器中
    QMutexLocker locker(&m_Mutex);
    QString qsConnName = connection.connectionName();
    if(m_queUseConnNames.contains(qsConnName))
    {
        m_queUseConnNames.removeOne(qsConnName);
        m_queUnuserConnNames.enqueue(qsConnName);
        //如果某一线程已使用完连接,另一线程正在申请一个数据库连接,进行唤醒操作
        m_WaitConn.wakeOne();
    }
}

QSqlDatabase CSQLDatabase::CreateConnect(const QString &qsConnName)
{
    //查看该连接名是否已经创建过连接
    if(QSqlDatabase::contains(qsConnName))
    {
        QSqlDatabase db = QSqlDatabase::database(qsConnName);
        return db;
    }

    QSqlDatabase db = QSqlDatabase::addDatabase(m_qsDatabaseType, qsConnName);
    db.setHostName(m_qsHostName);
    db.setDatabaseName(m_qsDatabase);
    db.setUserName(m_qsUser);
    db.setPassword(m_qsPassWd);
    db.setPort(m_nPort);

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

    return db;
}

bool CSQLDatabase::ExecuteStmt(QString qsSql)
{
    QSqlDatabase db = OpenConnection();
    if(!db.isOpen())
        return false;

    QSqlQuery query = db.exec(qsSql);
    CloseConnection(db);

    if(query.lastError().isValid())
    {
        qDebug() << "Sql error:" << query.lastError();
        return false;
    }

    return true;
}

QSqlQuery CSQLDatabase::ExecuteQuery(QString qsSql)
{
    QSqlDatabase db = OpenConnection();
    if(!db.isOpen())
        return QSqlQuery();

    QSqlQuery query = db.exec(qsSql);
    CloseConnection(db);

    if(query.lastError().isValid())
    {
        qDebug() << "Sql error:" << query.lastError();
        return QSqlQuery();
    }

    return query;
}

Qt 可以做什么?

Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部;Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。

大部分应用程序都可以使用 Qt 实现,除了与计算机底层结合特别紧密的,例如驱动开发,它直接使用硬件提供的编程接口,而不能使用操作系统自带的函数库。

下面的程序都使用 Qt 开发:WPS、YY语音、Skype、豆瓣电台、虾米音乐、淘宝助理、千牛、暴雪的战网客户端、VirtualBox、Opera、咪咕音乐、Google地图、Adobe Photoshop Album 等。

Linux 也是嵌入式的主力军,广泛应用于消费类电子、工业控制、军工电子、电信/网络/通讯、航空航天、汽车电子、医疗设备、仪器仪表等相关行业。

Qt 虽然也支持手机操作系统,但是由于 Android 本身已经有Java和 Kotlin,iOS 本身已经有 Objective-C 和 Swift,所以 Qt 在移动端的市场份额几乎可以忽略。

总起来说,Qt 主要用于桌面程序开发和嵌入式开发。