前面的章节里,我们使用了下面的函数创建和取得数据库连接:
需要维护连接的名字
获取连接的时候需要传入连接的名字
获取连接的时候不知道连接是否已经被使用,使用多线程的时候,每个线程都必须使用不同的连接
控制连接的最大数量比较困难,因为不能在程序里无限制的创建连接
连接断了后不会自动重连
删除连接不方便
这一节我们将创建一个简易的数据库连接池,就是为了解决上面的几个问题。使用数据库连接池后,只需要关心下面 3 个函数,而且刚刚提到的那些弊端都通过连接池解决了,对调用者是透明的。
<col>
功能
代码
获取连接
QSqlDatabase db = ConnectionPool::openConnection()
释放连接
ConnectionPool::closeConnection(db)
关闭连接池
ConnectionPool::release() // 一般在 main() 函数返回前调用
在具体介绍数据库连接池的实现之前,先来看看怎么使用。
获取连接时不需要了解连接的名字
支持多线程,保证获取到的连接一定是没有被其他线程正在使用
按需创建连接
可以创建多个连接
可以控制连接的数量
连接被复用,不是每次都重新创建一个新的连接
连接断开了后会自动重连
当无可用连接时,获取连接的线程会等待一定时间尝试继续获取,直到超时才会返回一个无效的连接
关闭连接很简单
数据库连接池的实现只需要 2 个文件:<code>ConnectionPool.h</code> 和 <code>ConnectionPool.cpp</code>。下面会列出文件的内容加以介绍。
<code>ConnectionPool.h</code>
<code>openConnection()</code> 用于从连接池里获取连接。
<code>closeConnection(QSqlDatabase connection)</code> 并不会真正的关闭连接,而是把连接放回连接池复用。连接的底层是通过 Socket 来通讯的,建立 Socket 连接是非常耗时的,如果每个连接都在使用完后就给断开 Socket 连接,需要的时候再重新建立 Socket连接是非常浪费的,所以要尽量的复用以提高效率。
<code>release()</code> 真正的关闭所有的连接,一般在程序结束的时候才调用,在 main() 函数的 return 语句前。
<code>usedConnectionNames</code> 保存正在被使用的连接的名字,用于保证同一个连接不会同时被多个线程使用。
<code>unusedConnectionNames</code> 保存没有被使用的连接的名字,它们对应的连接在调用 <code>openConnection()</code> 时返回。
如果 <code>testOnBorrow</code> 为 true,则连接断开后会自动重新连接(例如数据库程序崩溃了,网络的原因等导致连接断开了)。但是每次获取连接的时候都会先查询一下数据库,如果发现连接无效则重新建立连接。<code>testOnBorrow</code> 为 true 时,需要提供一条 SQL 语句用于测试查询,例如 MySQL 下可以用 <code>SELECT 1</code>。如果 <code>testOnBorrow</code> 为 false,则连接断开后不会自动重新连接。需要注意的是,Qt 里已经建立好的数据库连接当连接断开后调用 QSqlDatabase::isOpen() 返回的值仍然是 true,因为先前的时候已经建立好了连接,Qt 里没有提供判断底层连接断开的方法或者信号,所以 QSqlDatabase::isOpen() 返回的仍然是先前的状态 true。
<code>testOnBorrowSql</code> 为测试访问数据库的 SQL,一般是一个非常轻量级的 SQL,如 <code>SELECT 1</code>。
获取连接的时候,如果没有可用连接,我们的策略并不是直接返回一个无效的连接,而是等待 <code>waitInterval</code> 毫秒,如果期间有连接被释放回连接池里就返回这个连接,没有就继续等待 <code>waitInterval</code> 毫秒,再看看有没有可用连接,直到等待 <code>maxWaitTime</code> 毫秒仍然没有可用连接才返回一个无效的连接。
因为我们不能在程序里无限制的创建连接,用 <code>maxConnectionCount</code> 来控制创建连接的最大数量。
<code>ConnectionPool.cpp</code>