Russian Qt Forum

Qt => Базы данных => Тема начата: INZER от Май 30, 2022, 16:50



Название: Работа с БД в отдельном потоке
Отправлено: INZER от Май 30, 2022, 16:50
Решил вынести работу с базой данных в отдельный поток

Приложение строится на архитектуре Model-View-Presenter
В модели БД воркер (SqlWorker ниже), агрегирующий класс-исполнитель запросов (SqlQueryExecutor)
Код:

class SqlWorker : public QObject
{
    Q_OBJECT
public:
    explicit SqlWorker(QObject *parent = nullptr);
    ~SqlWorker ();

    void addDbServer (QString serverName, QHostAddress address);
    QList <QString> getDbServers ();

    QString getLastUsedDbName () { return currentDbName; }
    bool openDbConnection(QString serverName, QString login, QString password);
private:
    QString currentDbName;
    QHash<QString, QHostAddress> dbServers;

    QThread * executorThread;
    SqlQueryExecutor * executor;

signals:
    void stopExecutor ();
    void executorOpenDbConnection (QString, QString, QString, QString);
    void dbErrorOccured (STATE, QString, QString, bool);

private slots:
    void slotHandleDbError (qint8 error, QString errorDescription, QString dbName);
    void slotDbConnected (QString);

};

class SqlQueryExecutor : public QObject
{
    Q_OBJECT
public:
    explicit SqlQueryExecutor(QObject *parent = nullptr);
    ~SqlQueryExecutor ();
private:
    const QString strQueryA618;
    QString strWhereA618;
    A618SqlModel * model618;

    void changeModel618 (const QString& query);

signals:
    void dbConnected (QString);
    void dbDbErrorAppears (qint8 error, QString errorDescription, QString dbName);
    void return618Data (A618SqlModel & model);
    void finished ();

public slots:
    void slotStartExecutorThread ();
    void slotStopExecutorThread ();

    void slotOpenDbConnection (QString adress, QString serverName, QString login, QString password);
    void slotGet618Messages (unsigned int turplesLimit);
};

class A618SqlModel : public QSqlQueryModel
{
    Q_OBJECT

public:
    A618SqlModel(QObject *parent = 0);
    QVariant data(const QModelIndex &index, int role) const;
};

Q_DECLARE_METATYPE(A618SqlModel)

Проблема в том что ранее в однопоточном исполнении результат выполнения запроса попадал в модель A618SqlModel, котрая успешно скармливалась QtableView

Однако сейчас я не могу передать результат выполения запроса, так как не могу использовать A618SqlModel для передачи в виде параметра сигнала (не могу зарегистрировать его как метатип, потому что конструктор копирования закрыт).

Подскажите пожалуйста как правильно в Qt правильно передать результат выполния запроса к БД из другого потока.

Код:
QObject::connect(modelObject, SIGNAL(return618Data(A618SqlModel &)), this, SLOT(slotSet618Messages(A618SqlModel &)));

Код:
    qRegisterMetaType<A618SqlModel>();

Заранее спасибо


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Май 30, 2022, 17:18
Может, сделать наоборот - в вокер передать ссылку на TableView? Проблем с совместным использованием, вроде, не наблюдается.


Название: Re: Работа с БД в отдельном потоке
Отправлено: INZER от Май 30, 2022, 21:47
Спасибо, завтра обязательно попробую,
Была такая мысль, но сомневался в корректности доступа к табличке из другого потока.

Единственное что смущает - не нашел что у QTableView реализован конструктор копирования, как я понимаю без него  не получится зарегистрироовать мета тип и, соответственно, установить коннекты между методами объектов.


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Май 30, 2022, 22:00
Вы не поняли - я предлагаю использовать TableView из того потока, где вы работаете с базой. Т.е. в конструкторе вокера предусматриваете ссылку на вьюшник, ну и в конструкторе делаете tableView-> setModel(model618).


Название: Re: Работа с БД в отдельном потоке
Отправлено: INZER от Май 31, 2022, 07:11
Дело в том что у меня ~20 вью в которые необходимо отдавать результаты запросов. Не хочется хранить ссылки на вью в модели. Согласно архитектуре Model View Presenter отображение и модель не должны быть связаны друг с другом никак. Потому хочется прокидывать результат. Ну либо ссылку на требуемую вью непосредственно при запросе.


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Май 31, 2022, 09:09
Да хоть 40, какая разница? Массив указателей, в отдельном методе установка модели. Не понимаю проблемы. Но хозяин - барин...


Название: Re: Работа с БД в отдельном потоке
Отправлено: INZER от Май 31, 2022, 09:16
Ни в коем случае не ругаюсь на ваше предложение, огромное спасибо за подсказку, не думал о таком решении, так как мыслил в связи с выбранной архитектурой. Реализую как временное решение.

Может кто то еще подскажет решения как из потока получить данные из БД.


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Май 31, 2022, 09:45
так как мыслил в связи с выбранной архитектурой.
Архитектура тут не виновата ;)

Может кто то еще подскажет решения как из потока получить данные из БД.
Еще как подскажут, жалеть будете, что спросили :)


Название: Re: Работа с БД в отдельном потоке
Отправлено: INZER от Июнь 01, 2022, 16:02
Если кому то вдруг пригодится через СИГНАЛ-СЛОТ можно прокидвать указатели на любые типы данных :) Прокидываю указатель на QSqlQuery
Тему можно считать закрытой


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Июнь 01, 2022, 22:11
Не очень понятно, что вы сделали, но имейте в виду, что подключение к БД может быть использовано только в том потоке, в котором оно было создано.


Название: Re: Работа с БД в отдельном потоке
Отправлено: INZER от Июнь 01, 2022, 23:20
Да, спасибо я знаю

Результат возвращаю во вью через указатель на QSqlQuery

Код:
void SqlQueryExecutor::slotGet618Data(uint turplesCount)
{
    if (!QSqlDatabase::database().isOpen())
    {
        emit dbDbErrorAppears(QSqlError::ConnectionError, tr("Ошибка подключения к БД."), "");
        return;
    }
    QSqlQuery * query618 = new QSqlQuery ();
    QString sty;
    if (turplesCount == 0)
        sty = QString ("%1 %2 ORDER BY id DESC ").arg(strQueryA618, strWhereA618);
    else
        sty = QString ("%1 %2 ORDER BY id DESC %3 %4").arg(strQueryA618, strWhereA618, "LIMIT").arg(turplesCount);
    query618->exec(sty);
    if (query618->lastError().isValid())
    {
        emit dbDbErrorAppears(query618->lastError().type(), query618->lastError().text(), "");
        return;
    }
    emit set618Data(query618);
}


Название: Re: Работа с БД в отдельном потоке
Отправлено: sergek от Июнь 02, 2022, 08:31
Как я понял, модель у вас в гуёвом потоке? Так делать нельзя по той причине, о которой я вам напоминал.
Другой вопрос, можно ли перенести модель в поток БД и использовать ее для нескольких представлений, существующих в основном потоке? Не уверен, может, коллеги что подскажут?

Upd. Да, собственно, и не нужны подсказки. Тут простая логика - модель должна быть одна, связана с источником данных (БД) и находиться в потоке БД. Представлений может быть много и находиться они в любом потоке. Так что см. мой первый совет.


Название: Re: Работа с БД в отдельном потоке
Отправлено: diten от Июнь 21, 2022, 08:20
Нужно в том потоке создавать динамически экземпляр модели.
В методе инициализации окна:
connect(ссылка_на_объект_класса_потока_работающего_с_БД, SIGNAL(resultReady(QSqlQueryModel*)), this, SLOT(completeModel(QSqlQueryModel*)));
В определение класса, который работает с БД:
  QSqlQueryModel *model;
В методе run():
  model = new QSqlQueryModel();
  model->setQuery(qry, db);
  emit resultReady(this->model);
Это решение вашей проблемы. Но, есть в этом подводный камень, иногда выносит ошибку во время присвоения модели в QTableView. Пока в поисках, как это сделать правильно