My solution is to use QtConcurrent with a custom thread pool that does not destroy its threads. And in each thread's context, I create a dedicated QSqlDatabase connection with that thread's name as the connection's name such that each threads acquires the same connection each time it needs to talk to the database.
Setup:
mThreadPool = new QThreadPool(this);
// keep threads indefinitely so we don't loose QSqlDatabase connections:
mThreadPool->setExpiryTimeout(-1);
mThreadPool->setMaxThreadCount(10); /* equivalent to 10 connections */
qDebug() << "Maximum connection count is "
<< mThreadPool->maxThreadCount();
Destructor:
// remove the runnables that are not yet started
mThreadPool->clear();
// wait for running threads to finish (blocks)
delete mThreadPool;
Sample API implementation returning a future which can be used to get the data form the database when it is available:
QFuture<QList<ArticleCategory> *>
DatabaseService::fetchAllArticleCategories() const
{
return QtConcurrent::run(mThreadPool,
&DatabaseService::fetchAllArticleCategoriesWorker, mConnParameters);
}
Notice my solution does not manage the objects it creates.
The calling code needs to manage that memory (the QList returned above).
Accompanying thread worker function:
QList<ArticleCategory> *DatabaseService::fetchAllArticleCategoriesWorker(const DatabaseConnectionParameters &dbconparams)
{
try {
setupThread(dbconparams);
} catch (exceptions::DatabaseServiceGeneralException &e) {
qDebug() << e.getMessage();
return nullptr;
}
QString threadName = QThread::currentThread()->objectName();
QSqlDatabase db = QSqlDatabase::database(threadName, false);
if (db.isValid() && db.open()) {
QSqlQuery q(db);
q.setForwardOnly(true);
// ...
}
// else return nullptr
// ...
}
If you've noticed, setupThread is always called at the start of the worker which basically preps the database connection for the calling thread:
void DatabaseService::setupThread(const DatabaseConnectionParameters &connParams)
{
utilities::initializeThreadName(); // just sets a QObject name for this thread
auto thisThreadsName = QThread::currentThread()->objectName();
// check if this thread already has a connection to a database:
if (!QSqlDatabase::contains(thisThreadsName)) {
if (!utilities::openDatabaseConnection(thisThreadsName, connParams))
{
qDebug() << "Thread"
<< thisThreadsName
<< "could not create database connection:"
<< QSqlDatabase::database(thisThreadsName, false).lastError().text();
}
else
{
qDebug() << "Thread"
<< thisThreadsName
<< "successfully created a database connection.";
}
}
}