Qt中的数据库连接池

Posted

技术标签:

【中文标题】Qt中的数据库连接池【英文标题】:DB connection pool in Qt 【发布时间】:2017-11-16 05:34:23 【问题描述】:

我有一个 Qt 应用程序,它有一个 DatabaseService 单例,只有一个 QSqlDatabase 实例,许多线程应该使用这个 QSqlDatabase 实例,当某些线程使用 QSqlDatabase 实例时,互斥锁被锁定。但我知道这不是这项任务的最佳模式。

我应该在这里使用某种数据库连接池还是类似的东西?以及如何在 Qt 中实现它?

请提供一些例子。


UPD:

其实例已经拥有自己的线程的类,该类将有多个实例:

.cpp:

//...

QFuture<QMap<QString, QString>> future = QtConcurrent::run(DatabaseService::executeQuery, sqlCommand);

future.waitForFinished();

//...

数据库服务:

.h:

class DatabaseService

public:
    //...
    static QMap<QString, QString> executeQuery(QString command);

private:
    static QThreadStorage<QSqlDatabase> mDatabasePool;
    static QSqlDatabase getDatabase();

;

.cpp:

//...
QThreadStorage<QSqlDatabase> DatabaseService::mDatabasePool;

QSqlDatabase DatabaseService::getDatabase()

    if(DatabaseService::mDatabasePool.hasLocalData()) 
        return DatabaseService::mDatabasePool.localData();
     else 
        auto database = QSqlDatabase::addDatabase("QPSQL", QUuid::createUuid().toString());
        database.setHostName("hostName");
        database.setDatabaseName("databaseName");
        database.setUserName("user");
        database.setPassword("password");
        DatabaseService::mDatabasePool.setLocalData(database);
        return database;
    


QMap<QString,QString> DatabaseService::executeQuery(QString command)

    QSqlQuery query (DatabaseService::getDatabase());
    query.exec(command);

    //...

    return result;


//...

UPD 2:

main.cpp:

int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);

    MyServer server;

    server.listen(QHostAddress::Any, 1234);

    return a.exec();

我的服务器.h:

class MyServer : public QTcpServer

    Q_OBJECT

    explicit MyServer(QObject *parent = 0);
    ~MyServer();

protected:
    void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;

signals:
    void stopAll();
;

我的服务器.cpp:

MyServer::MyServer(QObject *parent) : QTcpServer(parent)




MyServer::~MyServer()
    emit stopAll();


void MyServer::incomingConnection(qintptr socketDescriptor)
    QThread* clientThread = new QThread;
    MyClient *client = new MyClient(socketDescriptor, this);

    client->moveToThread(clientThread);

    connect(clientThread, SIGNAL(started()), client, SLOT(process()));

    connect(client, SIGNAL(finished()), clientThread, SLOT(quit()));
    connect(this, SIGNAL(stopAll()), client, SLOT(stopFromServer()));
    connect(client, SIGNAL(finished()), client, SLOT(deleteLater()));
    connect(clientThread, SIGNAL(finished()), clientThread, SLOT(deleteLater()));

    clientThread->start();

myclient.h:

class MyClient : public QObject

    Q_OBJECT
public:
    explicit MyClient(int socketDescriptor, MyServer *server);
    ~MyClient();

private:
    QSslSocket* socket = NULL;

public slots:
    void process();

myclient.cpp:

void MyClient::process()
    //typical connection things
    connect(this->socket, SIGNAL(encrypted()), this, SLOT(ready()));


void MyClient::ready()
    connect(socket,SIGNAL(readyRead()),this, SLOT(newData()));

【问题讨论】:

【参考方案1】:

我制作了一个软件也使用数据库连接。我没有在我的应用程序上使用 QThread,而是使用 QFuture,线程将从全局线程池中获取(通常线程池的数量将是 CPU 线程的数量)。

并且在访问数据库时,每个池都会有自己的QSqlDatabase。我使用 QThreadStorage 作为 QSqlDatabase 的存储。

static QThreadStorage<QSqlDatabase> mDatabasePool;
....
QSqlDatabase Db::getDatabase()

    if(mDatabasePool.hasLocalData()) 
        return mDatabasePool.localData();
     else 
        auto database = QSqlDatabase::addDatabase(SQLDRIVERNAME[DBTYPE], QUuid::createUuid().toString());
        mDatabasePool.setLocalData(database);
        return database;
    

所以只有当线程没有连接时才会创建数据库连接。

对于完整的示例项目,您可以查看我的项目:https://github.com/apinprastya/sultan。数据库在 libdb 中,worker QFuture 在 libserver 中。您可以将其用作参考。但我不确定我的设计是否适合你。

【讨论】:

感谢您的回答。我在你的 github 中检查了你的示例和代码,现在我试图理解这个方案:所以,我有很多线程应该使用 DB。如果我需要从这些线程执行查询,我应该在DatabaseService 类中使用QFutureQtConcurrent::run 方法executeQuery()。接下来,executeQuery() 将调用与您完全相同的DatabaseService::getDatabase() 方法来获取QSqlDatabase 实例,然后executeQuery() 将使用获得的QSqlDatabase 实例执行查询。我哪里错了? 是的,你是对的。使用 QFuture,您无需关心线程的生命周期。 QFuture 将使用来自 QThreadPool::globalInstance() 的线程池。您可以通过在全局线程池上设置 setMaxThreadCount() 来增加线程池的数量。当 QFuture 完成任务并向您从数据库查询的主线程数据发送信号时,您还需要一个 QFutureWatcher 作为处理程序。 谢谢,请检查我在问题的 UPD 部分中发布的代码,是否都正确(除了没有QFutureWatcher,因为我想打电话给@ 987654333@ 并在同一个函数中得到它的结果)? 如果你已经有单独的线程来调用executeQuery,那么使用QFuture 将毫无用处。它将是线程创建另一个线程。你的线程是一个池还是在调用 executeQuery 时创建了一个新线程? >"It will be thread create another thread" 是的,我也是这么想的。你能澄清你的最后一个问题吗?

以上是关于Qt中的数据库连接池的主要内容,如果未能解决你的问题,请参考以下文章

在 QtConcurrent::run 中使用 QSqlDatabase 连接(伪连接池)

数据源,连接池,连接,jndi

连接池

使用Java中的动态代理实现数据库连接池

大白话聊懂Java中的连接池,用包装模式实现标准的DataSource数据源连接池

连接池数据源JNDI三者间的关系及用法