QSqlDatabase: How to avoid 'qt_sql_default_con

2020-03-27 16:54发布

问题:

Sorry if this is a trivial question but I have been trying to build a small .ui that used QSQLITE as database and that uses QTableView to show 4 columns on a default database file as example.

I debugged the problem in every side, changed logical operations of the SQL and restructured the constructor in a more simple way but the error still stays.

After finishing to set up all parameters I am getting this error:

QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.

And

QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.

I looked on several sources that describe this error such as this source, this other source. Also this was useful but still nothing happens. Official documentation suggests a "wrong" and "right" way to do that here. but the error still stays. After all these different options I brought back the code to a more concise way and I hope that someone can shed light on this issue.

Below the snipped of code:

mainwindow.h

private:
    QString temporaryFolder;
    dataInfo *mNewDatabaseImages;
    QSqlTableModel *mNewTableImages;

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    temporaryFolder = "/home/to/Desktop/tempDBFolder/tmp.db";
    QFile dbRem(temporaryFolder);
    dbRem.remove();
    mNewDatabaseImages = new dataInfo(this);
    mNewDatabaseImages->initDataBase(temporaryFolder);
    mNewDatabaseImages->confDataBase();
    mNewTableImages = new QSqlTableModel(this, mNewDatabaseImages->getDatabase());
    mNewTableImages->setTable("leftCamTable");
    mNewTableImages->select();
    ui->bookMarkTableView->setModel(mNewTableImages);
    ui->bookMarkTableView->showColumn(true);
}

datainfo.h

#ifndef DATAINFO_H
#define DATAINFO_H
#include <QObject>
#include <QSqlDatabase>
#include "imageparam.h"

class dataInfo : public QObject
{
    Q_OBJECT
public:
    explicit dataInfo(QObject *parent = nullptr);
    bool initDataBase(const QString &nameDB);
    bool confDataBase();
    bool addItem(ImageParam* imageItem);
    QSqlDatabase getDatabase();
private:
    QString mError;
    QSqlDatabase mDBImages;
};
#endif // DATAINFO_H

datainfo.cpp

#include "datainfo.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QVariant>
#include <QMessageBox>

#define CREATE_TABLE \
    " CREATE TABLE IF NOT EXISTS imageTable" \
    " (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
    " path1 TEXT NOT NULL" \
    " path2 TEXT NOT NULL" \
    " imageA BLOB NOT NULL" \
    " imageB BLOB NOT NULL)"

dataInfo::dataInfo(QObject *parent) : QObject(parent)
{}

bool dataInfo::initDataBase(const QString &nameDB)
{
    mDBImages = QSqlDatabase::addDatabase("QSQLITE");
    mDBImages.setDatabaseName(nameDB);
    bool ok = mDBImages.open();
    if(!ok) {
        mError = mDBImages.lastError().text();
        qDebug() << mError;
    }
    return ok;
}

bool dataInfo::confDataBase()
{
    QSqlQuery qry;
    bool ok = qry.exec(CREATE_TABLE);
    if(!ok) {
        mError = qry.lastError().text();
    }
    return ok;
}

bool dataInfo::addItem(ImageParam *imageItem)
{
    QSqlQuery qry;
    qry.prepare("INSERT INTO imageTable (path1, path2, imageA, imageB)" \
                " VALUES (?,?,?,?)");
    qry.addBindValue(imageItem->path1());
    qry.addBindValue(imageItem->path2());
    qry.addBindValue(imageItem->image1());
    qry.addBindValue(imageItem->image2());
    bool ok = qry.exec();
    if(!ok) {
        mError = qry.lastError().text();
    }
    return ok;
}

QSqlDatabase dataInfo::getDatabase()
{
    return mDBImages;
}

I looked also at this post that suggested to set a name to the databse first but I already do that in the function initDataBase(const QString &nameDB). Here the post that suggested the procedure, which is below:

db->setDatabaseName("name");
if(!db->open()) {
    qDebug() << "Error opening ";
    return false;
}

Please shed light on the possible solution.

回答1:

Short answer

  • You should specify the database you want to run your QSqlQuery on, otherwise they will run on the default database. You can specify the database in QSqlQuery's constructor with

    QSqlQuery query(QSqlDatabase::database("my-db"));

  • You are keeping a copy of the QSqlDatabase as a member of your dataInfo class, which will prevent it prevent closing properly. Instead, just use the static QSqlDatabase::database("name") when needed.

    auto db = QSqlDatabase::database("my-db");

Details

Providing the right database to QSqlQuery

Change all your uses of QSqlQuery. For example for confDataBase:

bool dataInfo::confDataBase()
{
    // Explicitly provide your database to the query
    // Otherwise the default database is used
    QSqlQuery qry(getDatabase());
    bool ok = qry.exec(CREATE_TABLE);
    if(!ok) {
        mError = qry.lastError().text();
    }
    return ok;
}

Not keeping QSqlDatabase attributes

From the documentation:

Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior.

Store your database's name in your class instead, and change your getDatabase to

dataInfo.cpp

bool dataInfo::initDataBase(const QString &nameDB)
{
    // Save database's name
    mDBName = nameDB;
    // Use the database locally, without storing it
    auto dbImages = QSqlDatabase::addDatabase("QSQLITE", nameDB);
    bool ok = dbImages.open();
    if(!ok) {
        mError = dbImages.lastError().text();
        qDebug() << mError;
    }
    return ok;
}

QSqlDatabase dataInfo::getDatabase()
{
    return QSqlDatabase::database(mDBName);
}

dataInfo.h

private:
    QString mError;
    QString mDBName;

Qt's code generating the warning

To have a look at the actual code producing the error: https://code.woboq.org/qt5/qtbase/src/sql/kernel/qsqldatabase.cpp.html#170

invalidateDb is used when connections are added or removed, and will trigger the error if the reference count > 1. As you are holding onto one, this will trigger the error.