精华内容
下载资源
问答
  • Qt>QSqlDatabase(数据库连接类)

    QSqlDatabase,代表了一个数据库连接,----官方文档


    展开全文
  • Qt 用QSqlDatabase 连接Firebird数据库

    千次阅读 2015-12-06 16:57:54
    QSqlDatabase db = QSqlDatabase :: addDatabase ( "QIBASE" ); db . setDatabaseName ( "E:/Test.fdb" ); db . setUserName ( "SYSDBA" ); db . setPassword ( " masterkey " ); if ( db . open ()...
    1、打开文件:D:\Qt\Qt5.1.1\5.1.1\Src\qtbase\src\plugins\sqldrivers\ibase\ibase.pro 
    
    2、修改里面的内容如下:     
      
    TARGET = qsqlibase
    OTHER_FILES += ibase.json
    HEADERS  = ../../../sql/drivers/ibase/qsql_ibase.h \
        ../../../sql/drivers/ibase/qsql_ibase_p.h
    SOURCES  = main.cpp \
        ../../../sql/drivers/ibase/qsql_ibase.cpp
    INCLUDEPATH += D:\Firebird-2.1.7.18553-0_Win32\include
    LIBS += D:\Firebird-2.1.7.18553-0_Win32\lib\fbclient_ms.lib
    unix:!contains( LIBS, .*gds.* ):!contains( LIBS, .*libfb.* ):LIBS    *= -lgds
    win32:!contains( LIBS, .*gds.* ):!contains( LIBS, .*fbclient.* ) {
     !win32-borland:LIBS *= -lfbclient_ms
     win32-borland:LIBS  += gds32.lib
    }
    include(../qsqldriverbase.pri)
    3、执行QMake;
    4、构建项目, 生成到此文件中D:\Qt\Qt5.1.1\5.1.1\Src\qtbase\plugins\sqldrivers\qsqlibased.dll
    5、将qsqlibased.dll复制到:D:\Qt\Qt5.1.1\5.1.1\msvc2010\plugins\sqldrivers文件夹中;
    6、新建工程测试:
      
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv); //此句必须有
        QSqlDatabase db = QSqlDatabase::addDatabase("QIBASE");
         db.setDatabaseName("E:/Test.fdb");
         db.setUserName("SYSDBA");
         db.setPassword("masterkey");
         if (db.open())
         {
          QMessageBox mb;
          mb.setText("ok");
          mb.exec();
          db.close();
         }
    }

    展开全文
  • QSqlDatabase操作数据库sqlite3

    千次阅读 2014-04-01 15:39:55
    基本打开数据库,执行SQL语句: static const char* DBName = "./test.db"; QFileInfo file(DBName); if(file.exists() == false) { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");

    基本打开数据库,执行SQL语句:

        static const char* DBName = "./test.db";
        QFileInfo file(DBName);
        if(file.exists() == false)
        {
            QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
            db.setDatabaseName(DBName); //设置数据库名
    
            if (!db.open())
            {
                qDebug("数据库不能打开");
                return -1;
            }
    
            QSqlQuery query;
            ///创建卡号数据库
            query.exec("CREATE TABLE tb_card(cardNum varchar(64) PRIMARY KEY, name varchar(64), job varchar(64))");
            query.clear();
         }

    select查询获取数据:

        QSqlQuery query(dbConn);
        QString sqlStr;
        sqlStr.append("select * from tb_card");
        bool isSuccess = query.exec(sqlStr);
    
        QString cardNum, name, job;
        int i = 0;
        while(query.next()) //query.next()指向查找到的第一条记录,然后每次后移一条记录
        {
            cardNum.clear();
            name.clear();
            job.clear();
            cardNum.append(query.value(0).toString());
            name.append(query.value(1).toString());
            job.append(query.value(2).toString());
        }


    记得在pro文件中加入:

    QT       += core gui sql 


    展开全文
  • Qt QSqlDataBase数据库连接线程池

    千次阅读 热门讨论 2017-07-26 10:28:14
    一般可用下面的函数创建和取得数据库连接:void createConnectionByName(const QString &connectionName) { QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", connectionName); db.setHostName("127.0.0.1...

    更正:之前的代码并不能实现多线程的连接池,因为获取的QSqlDatabase只能在当前线程中使用。以下是新的代码,不同的线程采用不同的连接池,以线程ID区分。需要在线程退出时调用releaseThreadPool()释放当前线程的连接池:

    // ConnectionPool.h
    #ifndef CONNECTIONPOOL_H
    #define CONNECTIONPOOL_H
    
    #include <QtSql>
    #include <QQueue>
    #include <QString>
    #include <QMutex>
    #include <QMutexLocker>
    
    class ConnectionPool {
    public:
        static void release(); // 关闭所有的数据库连接
        static QSqlDatabase openConnection();                 // 获取数据库连接
        static void closeConnection(QSqlDatabase connection); // 释放数据库连接回连接池
    
    	static void releaseThreadPool();						// 释放此线程下的全部连接池
    	
        ~ConnectionPool();
    
    private:
        static ConnectionPool& getInstance();
    
        ConnectionPool();
        ConnectionPool(const ConnectionPool &other);
        ConnectionPool& operator=(const ConnectionPool &other);
        QSqlDatabase createConnection(const QString &connectionName); // 创建数据库连接
    
        QQueue<QString> usedConnectionNames;   // 已使用的数据库连接名
        QQueue<QString> unusedConnectionNames; // 未使用的数据库连接名
    
        // 数据库信息
        QString hostName;
        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 ConnectionPool *instance;
    
        QMutex mutex;
        QWaitCondition waitConnection;
    
    	static QMutex g_thread_mutex;
    	static std::map<int, ConnectionPool *> g_thread_instances;
    };
    
    #endif // CONNECTIONPOOL_H
    
    
    // ConnectionPool.cpp
    #include "ConnectionPool.h"
    #include <QDebug>
    
    // QMutex ConnectionPool::mutex;
    // QWaitCondition ConnectionPool::waitConnection;
    // ConnectionPool* ConnectionPool::instance = NULL;
    
    QMutex ConnectionPool::g_thread_mutex;
    std::map<int, ConnectionPool *> ConnectionPool::g_thread_instances;
    
    ConnectionPool::ConnectionPool() {
        // 创建数据库连接的这些信息在实际开发的时都需要通过读取配置文件得到,
        // 这里为了演示方便所以写死在了代码里。
        hostName     = "127.0.0.1";
        databaseName = "qt";
        username     = "root";
        password     = "root";
        databaseType = "QMYSQL";
        testOnBorrow = true;
        testOnBorrowSql = "SELECT 1";
    
        maxWaitTime  = 1000;
        waitInterval = 200;
        maxConnectionCount  = 5;
    }
    
    ConnectionPool::~ConnectionPool() {
        // 销毁连接池的时候删除所有的连接
        foreach(QString connectionName, usedConnectionNames) {
            QSqlDatabase::removeDatabase(connectionName);
        }
    
        foreach(QString connectionName, unusedConnectionNames) {
            QSqlDatabase::removeDatabase(connectionName);
        }
    }
    
    ConnectionPool& ConnectionPool::getInstance() {
    	auto _tid = QThread::currentThreadId();
    	ConnectionPool* instance = NULL;
    	int _itid = reinterpret_cast<int>(_tid);
    
    	QMutexLocker locker(&g_thread_mutex);
    	auto itr = g_thread_instances.find(_itid);
    	if (g_thread_instances.empty() || g_thread_instances.end() == itr)
    	{
    		instance = new ConnectionPool();
    		g_thread_instances[_itid] = instance;
    	}
    	else
    	{
    		instance = itr->second;
    	}
    
    	//if (NULL == instance) {
    	//	QMutexLocker locker(&mutex);
    
    	//	if (NULL == instance) {
    	//		instance = new ConnectionPool();
    	//	}
    	//}
    
        return *instance;
    }
    
    void ConnectionPool::release() {
    	QMutexLocker locker(&g_thread_mutex);
    
    	//移除所有数据库连接
    	QStringList list = QSqlDatabase::connectionNames();
    	QList<QString>::Iterator it = list.begin(), itend = list.end();
    	for (; it != itend; it++) {
    		QSqlDatabase::removeDatabase(*it);
    	}
    
    	std::map<int, ConnectionPool *>::iterator itr = g_thread_instances.begin();
    	for (; itr != g_thread_instances.end(); itr++) {
    		auto _tmp = itr->second;
    		itr->second = NULL;
    		delete _tmp;
    		_tmp = NULL;
    	}
    
    	g_thread_instances.clear();
    
        // QMutexLocker locker(&mutex);
        // delete instance;
        // instance = NULL;
    }
    
    QSqlDatabase ConnectionPool::openConnection() {
        ConnectionPool& pool = ConnectionPool::getInstance();
        QString connectionName;
    
        QMutexLocker locker(&mutex);
    
        // 已创建连接数
        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 += pool.waitInterval) {
            waitConnection.wait(&mutex, pool.waitInterval);
    
            // 重新计算已创建连接数
            connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
        }
    
        if (pool.unusedConnectionNames.size() > 0) {
            // 有已经回收的连接,复用它们
            connectionName = pool.unusedConnectionNames.dequeue();
        } else if (connectionCount < pool.maxConnectionCount) {
            // 没有已经回收的连接,但是没有达到最大连接数,则创建新的连接
            connectionName = QString("Connection-%1").arg(connectionCount + 1);
        } else {
            // 已经达到最大连接数
            qDebug() << "Cannot create more connections.";
            return QSqlDatabase();
        }
    
        // 创建连接
        QSqlDatabase db = pool.createConnection(connectionName);
    
        // 有效的连接才放入 usedConnectionNames
        if (db.isOpen()) {
            pool.usedConnectionNames.enqueue(connectionName);
        }
    
        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);
            waitConnection.wakeOne();
        }
    }
    
    QSqlDatabase ConnectionPool::createConnection(const QString &connectionName) {
        // 连接已经创建过了,复用它,而不是重新创建
        if (QSqlDatabase::contains(connectionName)) {
            QSqlDatabase db1 = QSqlDatabase::database(connectionName);
    
            if (testOnBorrow) {
                // 返回连接前访问数据库,如果连接断开,重新建立连接
                qDebug() << "Test connection on borrow, execute:" << testOnBorrowSql << ", for" << connectionName;
                QSqlQuery query(testOnBorrowSql, db1);
    
                if (query.lastError().type() != QSqlError::NoError && !db1.open()) {
                    qDebug() << "Open datatabase error:" << db1.lastError().text();
                    return QSqlDatabase();
                }
            }
    
            return db1;
        }
    
        // 创建一个新的连接
        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;
    }
    
    void ConnectionPool::releaseThreadPool()
    {
    	auto _tid = QThread::currentThreadId();
    	ConnectionPool* instance = NULL;
    	int _itid = reinterpret_cast<int>(_tid);
    
    	QMutexLocker locker(&g_thread_mutex);
    	auto itr = g_thread_instances.find(_itid);
    	if (itr != g_thread_instances.end())
    	{
    		auto _tmp = itr->second;
    		g_thread_instances.erase(itr);
    		delete _tmp;
    		_tmp = NULL;
    	}
    }
    

    一般可用下面的函数创建和取得数据库连接:

    void createConnectionByName(const QString &connectionName) {
        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", connectionName);
        db.setHostName("127.0.0.1");
        db.setDatabaseName("qt"); // 如果是 SQLite 则为数据库文件名
        db.setUserName("root");   // 如果是 SQLite 不需要
        db.setPassword("root");   // 如果是 SQLite 不需要
    
    if (!db.open()) {
        qDebug() &lt;&lt; "Connect to MySql error: " &lt;&lt; db.lastError().text();
        return;
    }
    

    }

    QSqlDatabase getConnectionByName(const QString &connectionName) {
    return QSqlDatabase::database(connectionName);
    }

    虽然抽象出了连接的创建和获取,但是有几个弊端:
    • 需要维护连接的名字
    • 获取连接的时候需要传入连接的名字
    • 获取连接的时候不知道连接是否已经被使用,使用多线程的时候,每个线程都必须使用不同的连接
    • 控制连接的最大数量比较困难,因为不能在程序里无限制的创建连接
    • 连接断了后不会自动重连
    • 删除连接不方便

    这一节我们将创建一个简易的数据库连接池,就是为了解决上面的几个问题。使用数据库连接池后,只需要关心下面 3 个函数,而且刚刚提到的那些弊端都通过连接池解决了,对调用者是透明的。

    功能代码
    获取连接QSqlDatabase db = ConnectionPool::openConnection()
    释放连接ConnectionPool::closeConnection(db)
    关闭连接池ConnectionPool::release() // 一般在 main() 函数返回前调用

    数据库连接池的使用

    在具体介绍数据库连接池的实现之前,先来看看怎么使用。

    #include "ConnectionPool.h"
    #include <QDebug>
    

    void foo() {
    // 1. 从数据库连接池里取得连接
    QSqlDatabase db = ConnectionPool::openConnection();

    <span class="token comment">// 2. 使用连接查询数据库
    

    QSqlQuery query(db);
    query.exec(“SELECT * FROM user where id=1”);

    <span class="token keyword">while</span> <span class="token punctuation">(</span>query<span class="token punctuation">.</span><span class="token function">next<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">qDebug<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> query<span class="token punctuation">.</span><span class="token function">value<span class="token punctuation">(</span></span><span class="token string">"username"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token comment">// 3. 连接使用完后需要释放回数据库连接池
    

    ConnectionPool::closeConnection(db);
    }

    int main(int argc, char *argv[]) {
    foo();

    ConnectionPool<span class="token operator">::</span><span class="token function">release<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4. 释放数据库连接
    

    return 0;
    }

    数据库连接池的特点

    • 获取连接时不需要了解连接的名字
    • 支持多线程,保证获取到的连接一定是没有被其他线程正在使用
    • 按需创建连接
    • 可以创建多个连接
    • 可以控制连接的数量
    • 连接被复用,不是每次都重新创建一个新的连接
    • 连接断开了后会自动重连
    • 当无可用连接时,获取连接的线程会等待一定时间尝试继续获取,直到超时才会返回一个无效的连接
    • 关闭连接很简单

    数据库连接池的实现

    数据库连接池的实现只需要 2 个文件:ConnectionPool.hConnectionPool.cpp。下面会列出文件的内容加以介绍。


    ConnectionPool.h

    #ifndef CONNECTIONPOOL_H
    #define CONNECTIONPOOL_H
    

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

    class ConnectionPool {
    public:
    static void release(); // 关闭所有的数据库连接
    static QSqlDatabase openConnection(); // 获取数据库连接
    static void closeConnection(QSqlDatabase connection); // 释放数据库连接回连接池

    ~ConnectionPool();

    private:
    static ConnectionPool& getInstance();

    <span class="token function">ConnectionPool<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">ConnectionPool<span class="token punctuation">(</span></span><span class="token keyword">const</span> ConnectionPool <span class="token operator">&amp;</span>other<span class="token punctuation">)</span><span class="token punctuation">;</span>
    ConnectionPool<span class="token operator">&amp;</span> <span class="token keyword">operator</span><span class="token operator">=</span><span class="token punctuation">(</span><span class="token keyword">const</span> ConnectionPool <span class="token operator">&amp;</span>other<span class="token punctuation">)</span><span class="token punctuation">;</span>
    QSqlDatabase <span class="token function">createConnection<span class="token punctuation">(</span></span><span class="token keyword">const</span> QString <span class="token operator">&amp;</span>connectionName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 创建数据库连接
    
    QQueue <QString > usedConnectionNames ; // 已使用的数据库连接名 QQueue <QString > unusedConnectionNames ; // 未使用的数据库连接名 // 数据库信息 QString hostName ; QString databaseName ; QString username ; QString password ; QString databaseType ;
    <span class="token keyword">bool</span>    testOnBorrow<span class="token punctuation">;</span>    <span class="token comment">// 取得连接的时候验证连接是否有效
    

    QString testOnBorrowSql; // 测试访问数据库的 SQL

    int maxWaitTime; // 获取连接最大等待时间
    int waitInterval; // 尝试获取连接时等待间隔时间
    int maxConnectionCount; // 最大连接数

    static QMutex mutex;
    static QWaitCondition waitConnection;
    static ConnectionPool *instance;
    };

    #endif // CONNECTIONPOOL_H

    • openConnection() 用于从连接池里获取连接。
    • closeConnection(QSqlDatabase connection) 并不会真正的关闭连接,而是把连接放回连接池复用。连接的底层是通过 Socket 来通讯的,建立 Socket 连接是非常耗时的,如果每个连接都在使用完后就给断开 Socket 连接,需要的时候再重新建立 Socket连接是非常浪费的,所以要尽量的复用以提高效率。
    • release() 真正的关闭所有的连接,一般在程序结束的时候才调用,在 main() 函数的 return 语句前。
    • usedConnectionNames 保存正在被使用的连接的名字,用于保证同一个连接不会同时被多个线程使用。
    • unusedConnectionNames 保存没有被使用的连接的名字,它们对应的连接在调用 openConnection() 时返回。
    • 如果 testOnBorrow 为 true,则连接断开后会自动重新连接(例如数据库程序崩溃了,网络的原因等导致连接断开了)。但是每次获取连接的时候都会先查询一下数据库,如果发现连接无效则重新建立连接。testOnBorrow 为 true 时,需要提供一条 SQL 语句用于测试查询,例如 MySQL 下可以用 SELECT 1。如果 testOnBorrow 为 false,则连接断开后不会自动重新连接。需要注意的是,Qt 里已经建立好的数据库连接当连接断开后调用 QSqlDatabase::isOpen() 返回的值仍然是 true,因为先前的时候已经建立好了连接,Qt 里没有提供判断底层连接断开的方法或者信号,所以 QSqlDatabase::isOpen() 返回的仍然是先前的状态 true。
    • testOnBorrowSql 为测试访问数据库的 SQL,一般是一个非常轻量级的 SQL,如 SELECT 1
    • 获取连接的时候,如果没有可用连接,我们的策略并不是直接返回一个无效的连接,而是等待 waitInterval 毫秒,如果期间有连接被释放回连接池里就返回这个连接,没有就继续等待 waitInterval 毫秒,再看看有没有可用连接,直到等待 maxWaitTime 毫秒仍然没有可用连接才返回一个无效的连接。
    • 因为我们不能在程序里无限制的创建连接,用 maxConnectionCount 来控制创建连接的最大数量。

    ConnectionPool.cpp

    #include "ConnectionPool.h"
    #include <QDebug>
    

    QMutex ConnectionPool::mutex;
    QWaitCondition ConnectionPool::waitConnection;
    ConnectionPool* ConnectionPool::instance = NULL;

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

    maxWaitTime  <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
    waitInterval <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span>
    maxConnectionCount  <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span>
    

    }

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

    <span class="token function">foreach<span class="token punctuation">(</span></span>QString connectionName<span class="token punctuation">,</span> unusedConnectionNames<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        QSqlDatabase<span class="token operator">::</span><span class="token function">removeDatabase<span class="token punctuation">(</span></span>connectionName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    ConnectionPool& ConnectionPool::getInstance() {
    if (NULL == instance) {
    QMutexLocker locker(&mutex);

        <span class="token keyword">if</span> <span class="token punctuation">(</span>NULL <span class="token operator">==</span> instance<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token function">ConnectionPool<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">return</span> <span class="token operator">*</span>instance<span class="token punctuation">;</span>
    

    }

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

    QSqlDatabase ConnectionPool::openConnection() {
    ConnectionPool& pool = ConnectionPool::getInstance();
    QString connectionName;

    QMutexLocker <span class="token function">locker<span class="token punctuation">(</span></span><span class="token operator">&amp;</span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// 已创建连接数
    

    int connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();

    <span class="token comment">// 如果连接已经用完,等待 waitInterval 毫秒看看是否有可用连接,最长等待 maxWaitTime 毫秒
    

    for (int i = 0;
    i < pool.maxWaitTime
    && pool.unusedConnectionNames.size() 0 && connectionCount pool.maxConnectionCount;
    i += pool.waitInterval) {
    waitConnection.wait(&mutex, pool.waitInterval);

        <span class="token comment">// 重新计算已创建连接数
    

    connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
    }

    <span class="token keyword">if</span> <span class="token punctuation">(</span>pool<span class="token punctuation">.</span>unusedConnectionNames<span class="token punctuation">.</span><span class="token function">size<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 有已经回收的连接,复用它们
    

    connectionName = pool.unusedConnectionNames.dequeue();
    } else if (connectionCount < pool.maxConnectionCount) {
    // 没有已经回收的连接,但是没有达到最大连接数,则创建新的连接
    connectionName = QString(“Connection-%1”).arg(connectionCount + 1);
    } else {
    // 已经达到最大连接数
    qDebug() << “Cannot create more connections.”;
    return QSqlDatabase();
    }

    <span class="token comment">// 创建连接
    

    QSqlDatabase db = pool.createConnection(connectionName);

    <span class="token comment">// 有效的连接才放入 usedConnectionNames
    

    if (db.isOpen()) {
    pool.usedConnectionNames.enqueue(connectionName);
    }

    <span class="token keyword">return</span> db<span class="token punctuation">;</span>
    

    }

    void ConnectionPool::closeConnection(QSqlDatabase connection) {
    ConnectionPool& pool = ConnectionPool::getInstance();
    QString connectionName = connection.connectionName();

    <span class="token comment">// 如果是我们创建的连接,从 used 里删除,放入 unused 里
    

    if (pool.usedConnectionNames.contains(connectionName)) {
    QMutexLocker locker(&mutex);
    pool.usedConnectionNames.removeOne(connectionName);
    pool.unusedConnectionNames.enqueue(connectionName);
    waitConnection.wakeOne();
    }
    }

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

        <span class="token keyword">if</span> <span class="token punctuation">(</span>testOnBorrow<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 返回连接前访问数据库,如果连接断开,重新建立连接
    

    qDebug() << “Test connection on borrow, execute:” << testOnBorrowSql << “, for” << connectionName;
    QSqlQuery query(testOnBorrowSql, db1);

            <span class="token keyword">if</span> <span class="token punctuation">(</span>query<span class="token punctuation">.</span><span class="token function">lastError<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">type<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">!=</span> QSqlError<span class="token operator">::</span>NoError <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>db1<span class="token punctuation">.</span><span class="token function">open<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token function">qDebug<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> <span class="token string">"Open datatabase error:"</span> <span class="token operator">&lt;&lt;</span> db1<span class="token punctuation">.</span><span class="token function">lastError<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">return</span> <span class="token function">QSqlDatabase<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    
        <span class="token keyword">return</span> db1<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token comment">// 创建一个新的连接
    

    QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, connectionName);
    db.setHostName(hostName);
    db.setDatabaseName(databaseName);
    db.setUserName(username);
    db.setPassword(password);

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>db<span class="token punctuation">.</span><span class="token function">open<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">qDebug<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> <span class="token string">"Open datatabase error:"</span> <span class="token operator">&lt;&lt;</span> db<span class="token punctuation">.</span><span class="token function">lastError<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token function">QSqlDatabase<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">return</span> db<span class="token punctuation">;</span>
    

    }

    为了支持多线程,使用了 QMutex,QWaitCondition 和 QMutexLocker 来保护共享资源 usedConnectionNames 和 unusedConnectionNames 的读写。

    在构造函数里初始化访问数据库的信息和连接池的配置,为了方便所以都硬编码写在了代码里,实际开发的时候这么做是不可取的,都应该从配置文件里读取,这样当它们变化后只需要修改配置文件就能生效,否则就需要修改代码,然后编译,重新发布等。虚构函数里真正的把所有连接和数据库断开。

    ConnectionPool 使用了 Singleton 模式,保证在程序运行的时候只有一个对象被创建,getInstance() 用于取得这个唯一的对象。按理说使用 openConnection() 的方法在 Singleton 模式下的调用应该像这样 ConnectionPool::getInstance().openConnection(),但是我们实现的却是 ConnectionPool::openConnection(),因为我们把 openConnection() 也定义成静态方法,在它里面调用 getInstance() 访问这个对象的数据,这样做的好处即使用了 Singleton 的优势,也简化了 openConnection() 的调用。

    调用 ConnectionPool::release() 会删除 ConnectionPool 唯一的对象,在其虚构函数里删除所有的数据库连接。

    openConnection() 函数相对比较复杂,也是 ConnectionPool 的核心
    1. 如果没有可复用连接 pool.unusedConnectionNames.size() == 0 且已经创建的连接数达到最大,则等待,等待期间有连接被释放回连接池就复用这个连接,如果超时都没有可用连接,则返回一个无效的连接 QSqlDatabase()
    2. 如果没有可复用连接,但是已经创建的连接数没有达到最大,那么就创建一个新的连接,并把这个连接的名字添加到 usedConnectionNames
    3. 如果有可复用的连接,则复用它,把它的名字从 unusedConnectionNames 里删除并且添加到 usedConnectionNames
    createConnection() 是真正创建连接的函数
    1. 如果连接已经被创建,不需要重新创建,而是复用它。testOnBorrow 为 true 的话,返回这个连接前会先用 SQL 语句 testOnBorrowSql 访问一下数据库,没问题就返回这个连接,如果出错则说明连接已经断开了,需要重新和数据库建立连接。
    2. 如果连接没有被创建过,才会真的建立一个新的连接。
    closeConnection() 并不是真的断开连接
    1. 需要判断连接是否我们创建的,如果不是就不处理。
    2. 把连接的名字从 usedConnectionNames 里删除并放到 unusedConnectionNames 里,表示这个连接已经被回收,可以被复用了。
    3. 唤醒一个等待的线程,告诉它有一个连接可用了。

    测试

    测试用例:连接池允许最多创建 5 个连接,我们启动 10 个线程用连接池里获取连接访问数据库。

    ConnectionTestThread.h

    #ifndef CONNECTIONTESTTHREAD_H
    #define CONNECTIONTESTTHREAD_H
    #include <QThread>
    

    class ConnectionTestThread : public QThread {
    protected:
    void run();
    };

    #endif // CONNECTIONTESTTHREAD_H

    ConnectionTestThread.cpp

    #include "ConnectionTestThread.h"
    #include "ConnectionPool.h"
    

    void ConnectionTestThread::run() {
    // 从数据库连接池里取得连接
    QSqlDatabase db = ConnectionPool::openConnection();
    qDebug() << “In thread run():” << db.connectionName();

    QSqlQuery <span class="token function">query<span class="token punctuation">(</span></span>db<span class="token punctuation">)</span><span class="token punctuation">;</span>
    query<span class="token punctuation">.</span><span class="token function">exec<span class="token punctuation">(</span></span><span class="token string">"SELECT * FROM user where id=1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">while</span> <span class="token punctuation">(</span>query<span class="token punctuation">.</span><span class="token function">next<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">qDebug<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> query<span class="token punctuation">.</span><span class="token function">value<span class="token punctuation">(</span></span><span class="token string">"username"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token comment">// 连接使用完后需要释放回数据库连接池
    

    ConnectionPool::closeConnection(db);
    }

    main.cpp

    #include "ConnectionTestThread.h"
    #include "ConnectionPool.h"
    

    #include <QApplication>
    #include <QPushButton>

    int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QPushButton <span class="token operator">*</span>button <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token function">QPushButton<span class="token punctuation">(</span></span><span class="token string">"Access Database"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    button<span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">show<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    QObject<span class="token operator">::</span><span class="token function">connect<span class="token punctuation">(</span></span>button<span class="token punctuation">,</span> <span class="token operator">&amp;</span>QPushButton<span class="token operator">::</span>clicked<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            ConnectionTestThread <span class="token operator">*</span>thread <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token function">ConnectionTestThread<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            thread<span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">start<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">int</span> ret <span class="token operator">=</span> a<span class="token punctuation">.</span><span class="token function">exec<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ConnectionPool<span class="token operator">::</span><span class="token function">release<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 程序结束时关闭连接,以免造成连接泄漏
    
    return ret ; }

    执行程序,点击按钮 Access Database,输出如下:

    In thread run(): Connection-1
    Alice
    In thread run(): Connection-2
    Alice
    In thread run(): Connection-3
    Alice
    In thread run(): Connection-4
    Alice
    In thread run(): Connection-5
    Test connection on borrow, execute: SELECT 1 , for Connection-1
    Alice
    In thread run(): Connection-1
    Test connection on borrow, execute: SELECT 1 , for Connection-2
    Alice
    In thread run(): Connection-2
    Test connection on borrow, execute: SELECT 1 , for Connection-3
    Alice
    In thread run(): Connection-3
    Test connection on borrow, execute: SELECT 1 , for Connection-4
    Alice
    In thread run(): Connection-4
    Test connection on borrow, execute: SELECT 1 , for Connection-5
    Alice
    In thread run(): Connection-5
    Alice

    可以看到,前 5 个连接是新创建的,后面 5 个连接复用了已经创建的连接。
    可以再做一下几个测试,看看连接池是否都能正确的运行。

    Case 1
    1. 点击按钮 Access Database,正常输出。
    2. 然后关闭数据库,点击按钮 Access Database,应该提示连不上数据库。
    3. 启动数据库,点击按钮 Access Database,正常输出。
    Case 2
    • 把线程数增加到 100 个,1000 个。
    • 同时测试关闭和再次打开数据库。
    Case 3
    • 在线程的 run() 函数里随机等待一段时间,例如 0 到 100 毫秒。

    数据库连接池基本已经完成,但是并不是很完善。考虑一下如果我们设置最大连接数为 100,高峰期访问比较多,创建满了 100 个连接,但是当闲置下来后可能只需要 2 个连接,其余 98 个连接都不长时间不用,但它们一直都和数据库保持着连接,这对资源(Socket 连接)是很大的浪费。需要有这样的机制,当发现连接一段时间没有被使用后就把其关闭,并从 unusedConnectionNames 里删除。还有例如连接被分配后没有释放回连接池,即一直在 usedConnectionNames 里面,即连接泄漏,超过一定时间后连接池应该主动把其回收。怎么实现这些的功能,这里就不在一一说明,大家独自思考一下应该怎么实现这些功能。

    展开全文
  • 1.sqlTableModeDemo为源码包,不要使用shadowbuild,通知注意mainwindow第26行的发布时和编译时数据库的使用位置; 2.app为独立可运行包,点击即可运行,方便调试与测试。 作者:红胖子/红模仿 QQ:21497936 ...
  • QSqlDatabase数据库

    千次阅读 2017-03-05 10:31:55
    #include <QSqlDatabase> #include #include #include #include <QSqlRecord>//创建数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "sqlite1"); db.setHostName("acid
  • 详解QT数据库类,详细讲解了QT连接Access的具体操作,以及个人的一些理解
  • Qt连接数据库报错QSqlDatabase: QMYSQL driver not loaded问题解决方法百度和教程里老师的说的解决方法 连接数据库时报错QSqlDatabase: QMYSQL driver not loaded 百度和教程里老师的说的解决方法 教程里老师给了一...
  • QT数据库连接必备QSqlDatabase

    千次阅读 2018-05-17 10:10:19
    每个应用都需要或多或少的保存一些数据,当不想...下面来介绍一个QSqlDatabase类(不是操作数据库,只连接数据库,操作数据请看数据库操作):QSqlDatabase创建连接QSqlDatabase QSqlDatabase::addDatabase(const QS...
  • Qt连接数据库的方法

    千次阅读 2020-08-15 12:32:00
    Qt连接数据库的两种方法 我曾经想过,无论在哪个平台下开发,都不要...简单来说,QSqlDatabase连接数据库可以分为两种方式,聊到这两种方式,就要大概的说一下数据访问的前因后果,以微软的数据访问历史为例,本文只
  • QSqlDatabase 连接 cloneDatabase()

    千次阅读 2015-10-23 15:43:53
    Qt中的QSqlDatabase类表示一个数据库连接。  数据库连接的创建通过静态方法addDatabase来实现,在创建时可以给数据库连接指定一个名称(ConnectionName),如果不指定名称也可以,会创建一个默认连接。如: ...
  • 我曾经想过,无论在哪个平台下开发,都不要再接触SQL Server了,但显然不行。我们是来看世界的,不是来改变世界的,想通就好。 前两天,尝试了一下Qt下远程访问数据库。...简单来说,QSqlDatabase连接数据库可以分...
  • QSqlDatabase类实现了数据库连接的操作 QSqlQuery类用来执行SQL语句 QSqlRecord类 封装数据库所有记录 第一:QSqlDatabaseQSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); 采用QSQLITE数据库,...
  • 一般来说,PyQt可用如下格式连接MySQL数据库: self.DB = QSqlDatabase.addDatabase('QMYSQL') self.DB.setDatabaseName("school_club") # 输入自己想访问的数据表 self.DB.setHostName('localhost') self.DB.set...
  • QT 009 QSqlDatabase 数据库类的使用

    千次阅读 2017-06-14 15:56:56
    前言:本文对QT的构建数据库连接类的帮助文件做了一点小结,可以看作是翻译吧: Qt 5.9 Qt SQL C++ Classes QSqlDatabase Qt 5.9.0 Reference Documentation ----------------------------------------------------...
  • 1.到Qt安装目录下,找到ODBC驱动文件(D:\Qt\Qt5.11.1\5.11.1\msvc2017_64\plugins\...2.在个人QT工程的目标程序目录下建立文件夹sqldrivers,并将对应数据库的驱动文件放进去,文件夹名字一定要是sqldrivers ...
  • 二、代码 #ifndef DATABASEACCESS_H #define DATABASEACCESS_H #include <QObject> #include <...QSqlDatabase> #include <QSqlQuery> #include <QMap> #include <QVe...
  • * 数据库连接池特点: * 获取连接时不需要了解连接的名字,连接池内部维护连接的名字 * 支持多线程,保证获取到的连接一定是没有被其他线程正在使用 * 按需创建连接,可以创建多个连接,可以控制连接的数量 * 连接...
  • 开源版的QT没有提供Mysql的驱动,导致使用数据库的时候提示如下错误: QSqlDatabase: QODBC driver not loaded QSqlDatabase: available drivers: QSQLITE 解决方法:重新编译QT  ./configure -qt-sql-mysql -...
  • 1.首先要你用QSqlDatabase连接数据库,连接上了才能获取数据库中的表: QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL"); db.setHostName("acidalia"); db.setDatabaseName("customdb"); db.setUserName(...
  • qt连接mysql数据库需要两个动态库。1:qt连接mysql的动态库--qsqlmysql.dll qsqlmysqld.dll;2:mysql的C connecor动态库--libmysql.dll。 缺少任何库都会提示:QSqlDatabase: QMYSQL driver not loaded,不过...
  • QT连接数据库

    万次阅读 多人点赞 2019-06-07 11:15:09
    QT连接数据库及操作 MySQL数据库 创建一个新的项目,带ui,(Widget) 在.pro文件中加上sql,如图 在类的.cpp中加上两个头文件 #include <QSqlDatabase> #include <QDebug> #include <QMessageBox&...
  • Ubuntu16.04下Qt5.9.4连接数据库失败,如图: [img=https://img-bbs.csdn.net/upload/201804/24/1524571138_611625.png][/img] 尝试过的解决办法: 1、自己手动编译mysql驱动的源码,生成libqsqlmysql.so库 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,186
精华内容 1,274
关键字:

qsqldatabase连接数据库