Based on the previously asked question and the answer found to it, I created a global static mutex, with which I regulate multithreaded access to the static methods of the QSqlDatabase class: addDatabase() , database() and removeDatabase() .

 #include <QtCore/QGlobalStatic> #include <QtCore/QMutex> Q_GLOBAL_STATIC(QMutex, _g_mutex) static QSqlDatabase addDatabase(const QString &conn_name) { QMutexLocker locker(_g_mutex); return QSqlDatabase::addDatabase(QLatin1String(DB_DRIVER), conn_name); } static QSqlDatabase database(const QString &conn_name) { QMutexLocker locker(_g_mutex); return QSqlDatabase::database(conn_name, false); } static void closeDatabase(const QString &conn_name) { QSqlDatabase db = database(conn_name); if(db.isValid() && db.isOpen()) db.close(); } static void removeDatabase(const QString &conn_name) { QMutexLocker locker(_g_mutex); QSqlDatabase::removeDatabase(conn_name); } 

It works without errors, there are no inter-thread conflicts, however, in my opinion, the number of locks executed by the mutex becomes excessive, in my opinion. The fact is that the QSqlDatabase object is implicit shared and is distributed on the stack, and to execute the query to the database, you must first get it. And when a QSqlQuery object is QSqlQuery , it has to call its own static database() method presented above each time. For example:

 bool MyDatabase::query(const QString &qstr) { QSqlQuery query(database(connectionName())); if(!query.exec(qstr)) return false; ... return true; } 

The MyDatabase::query() method is called multithreaded, and from this comes a lot of forced mutex locks, which, in theory, could have been avoided if we had a QSqlDatabase object existing between MyDatabase::query() calls. For example:

 class MyDatabase { public: MyDatabase() {} bool query(const QString &qstr); private: QSqlDatabase _db; }; 

In this case, we get the ideal option, when once we open a connection to the database, we block the mutex for a short time, and then we use the QSqlDatabase object without any locks and related brakes.

But here the problem is again: none of the QSqlDatabase objects associated with a specific database connection should exist immediately before closing the connection itself. The following are examples from Qt Help.

Wrong option:

 QSqlDatabase db = QSqlDatabase::database("sales"); QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db); QSqlDatabase::removeDatabase("sales"); 

... and correct:

 { QSqlDatabase db = QSqlDatabase::database("sales"); QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db); } QSqlDatabase::removeDatabase("sales"); 

It turns out that there is no way out except how to create a QSqlDatabase object on the heap so that, when it is no longer needed, it is correctly deleted. But how correct is this option? I think it would be against the idea of ​​using QSqlDatabase . I would appreciate assistance in resolving the current dilemma. It is also possible that there is an alternative path that is not obvious to me.

    1 answer 1

    Solved the problem as follows. I created a private DatabasePrivate wrapper class over QSqlDatabase and put it into a shared pointer. The wrapper is needed in order not to create a QSqlDatabase object on the heap, since there is no certainty that this approach is correct.

    Header file database.h :

     #include <QtCore/QSharedPointer> class DatabasePrivate; class Database { public: Database() {} ~Database(); private: QSharedPointer<DatabasePrivate> _d; }; 

    It should be noted that the implementation of the destructor is removed from the header file to the implementation file. This is necessary because the DatabasePrivate class has a forward declaration , while the template QSharedPointer requires complete information about the object it controls for correct deletion.

    Implementation file database.cpp :

     #include <QtCore/QGlobalStatic> #include <QtCore/QMutex> Q_GLOBAL_STATIC(QMutex, _g_mutex) class DatabasePrivate { public: QSqlDatabase _db; }; QSqlDatabase addDatabase(const QString &cname) { QMutexLocker locker(_g_mutex); return QSqlDatabase::addDatabase(QStringLiteral(DB_DRIVER), cname); } void removeDatabase(const QString &cname) { QMutexLocker locker(_g_mutex); QSqlDatabase::removeDatabase(cname); } void deletePrivateObject(DatabasePrivate *obj) { if(obj->_db.isValid()) { const QString cname = obj->_db.connectionName(); if(obj->_db.isOpen()) obj->_db.close(); delete obj; removeDatabase(cname); } } Database::Database() : _d(QSharedPointer<DatabasePrivate>(new DatabasePrivate() , deletePrivateObject)) {} Database::~Database() {} 

    Using QSharedPointer creates a problem that occurs when you start copying objects of the Database class, for example, through assignment. Each copy of the Database object will refer to the same QSqlDatabase object. If in the Database destructor to try to break the connection to the database server, then the surviving QSqlDatabase object will become invalid.

    For this reason, the Database destructor is empty, but the special function deletePrivateObject(DatabasePrivate*) , which is inserted by the second argument when creating a smart pointer, and will be called only when the copy counter of QSharedPointer equals zero.

    Thus, complete automation has been achieved, which does not require tracking the lifetime of the last copy of the QSqlDatabase object and correctly closes the database connection.

    The global mutex is now only required when opening and closing a database connection. When performing queries, we simply use the QSharedPointer object stored in QSharedPointer .