Qt数据库连接池

.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "connectionpool.h"
#include <QDebug>
#include <QSettings>
QMutex ConnectionPool::mutex;
QWaitCondition ConnectionPool::waitConnection;
ConnectionPool* ConnectionPool::instance = NULL;

ConnectionPool::ConnectionPool() {
// 创建数据库连接的这些信息在实际开发的时都需要通过读取配置文件得到,
// 这里为了演示方便所以写死在了代码里。
// hostName = "192.168.16.200";
// port=3306;
// databaseName = "web";
// username = "root";
// password = "zz191025ZZ";
QString apppath=QCoreApplication::applicationDirPath();
QString mysqlconfig=apppath+"/CFG/mysqlconfig.ini";
QSettings* m_IniFile = new QSettings(mysqlconfig, QSettings::IniFormat);
m_IniFile ->beginGroup("mysqlserver"); // 设置当前节名,代表以下的操作都是在这个节中
hostName= m_IniFile->value("ip").toString();
port=m_IniFile->value("port").toInt();
databaseName=m_IniFile->value("database").toString();
username=m_IniFile->value("username").toString();
password=m_IniFile->value("password").toString();
m_IniFile->endGroup();
m_IniFile->deleteLater();
databaseType = "QMYSQL";
testOnBorrow = true;
testOnBorrowSql = "SELECT 1";
waitInterval = 1000;
maxConnectionCount = 10;
maxWaitTime = waitInterval*maxConnectionCount;
}

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

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

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

if (NULL == instance) {
instance = new ConnectionPool();
}
}

return *instance;
}

void ConnectionPool::release() {
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();
// }
//此处修改为当没有可用连接并且连接数超出最大连接数,则一直循环等待,直到别的线程释放
while(pool.unusedConnectionNames.size() == 0 && connectionCount == pool.maxConnectionCount){
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.setPort(port);
db.setDatabaseName(databaseName);
db.setUserName(username);
db.setPassword(password);

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

return db;
}

.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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();

ConnectionPool();
ConnectionPool(const ConnectionPool &other);
ConnectionPool& operator=(const ConnectionPool &other);
QSqlDatabase createConnection(const QString &connectionName); // 创建数据库连接

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

// 数据库信息
QString hostName;
int port;
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;
};


#endif // CONNECTIONPOOL_H

QSqldatabase::addDatabase(“connectionname”)创建的数据库连接对象,只能在同一个线程中使用,但是一旦按照某个连接名创建了连接,那么按照QSqldatabase::database(“connectionname”)获取的连接实例可以跨线程使用,但是注意跨线程时不同线程同时使用一个连接名创建的连接,则会引起资源竞争,导致意外的错误,具体的错误为:2006:server has gone away 或者2013 lost connection,需要mutex锁保护一下,或者使用数据库连接池的概念,保证每次使用的连接没有和其他线程产生竞争。


Qt数据库连接池
http://yoursite.com/2019/01/15/Qt数据库连接池/
作者
还在输入
发布于
2019年1月15日
许可协议