Russian Qt Forum
Ноябрь 23, 2024, 15:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Решено: Результаты Mysql запросов и многопоточность приложения  (Прочитано 4944 раз)
soiam
Гость
« : Апрель 11, 2013, 10:54 »

Изначально создал класс для работы с БД mysql в потоке, в котором вертится gui. Но увидел недостатки этого: тормоза gui, при отсутствии коннекта мертвые подвисания. И решил перевести работу с БД в отдельный QThread.
Создал объект БД в функции run QThread, завел сингалы созданного QThread на слоты БД(для создания подключения, для формирования запроса, команды).

Вопрос в следующем, как мне считать результаты запросов БД по требованию из потока, в котором вертится GUI? Все результаты содержаться в QSqlQuery, который находится в другом потоке.
« Последнее редактирование: Апрель 11, 2013, 13:12 от soiam » Записан
mutineer
Гость
« Ответ #1 : Апрель 11, 2013, 11:13 »

Кода бы...
Записан
soiam
Гость
« Ответ #2 : Апрель 11, 2013, 11:29 »

Класс QThread
Код:
class QDatabase_Manager : public QThread
{
public:
//подготовить и послать команду SQL
    bool prepexec(const QString & query_cmd, QList<QNero_DataBase::QUERY_PARAM_T> * params = NULL,bool gen_sig = true,bool remote_mode = true);

.........
private:
    QNero_DataBase * nero_db;
}

//Создание обхекта БД и подключение слотов
void QDatabase_Manager::run(void)
{
    nero_db = new QNero_DataBase();
    connect(this,SIGNAL(signal_connect(bool,QString,QString,QString)),nero_db,SLOT(slot_connect(bool,QString,QString,QString)),Qt::QueuedConnection);
    connect(this,SIGNAL(signal_disconnect(bool)),nero_db,SLOT(slot_disconnect(bool)));
    connect(this,SIGNAL(signal_exec(QString,QList<QNero_DataBase::QUERY_PARAM_T>*,bool,bool)),nero_db,SLOT(slot_exec(QString,QList<QNero_DataBase::QUERY_PARAM_T>*,bool,bool)),Qt::QueuedConnection);
    connect(nero_db,SIGNAL(signal_write_log(QString)),this,SIGNAL(signal_write_log(QString)),Qt::QueuedConnection);
    connect(nero_db,SIGNAL(signal_set_connected(bool,bool)),this,SLOT(slot_connected(bool,bool)),Qt::QueuedConnection);
    connect(nero_db,SIGNAL(signal_set_done(bool)),this,SLOT(slot_set_done(bool)),Qt::QueuedConnection);
    QThread::exec();
    ........
    delete nero_db;
}

//функция подготовки команды запроса(query_cmd) и ее параметров(params, если они есть) к отправке в sql(удаленная mysql по умолчанию)
void QDatabase_Manager::prepexec(const QString & query_cmd, QList<QNero_DataBase::QUERY_PARAM_T> * params,bool gen_sig, bool remote_mode)
{
    done = false;
    data_error = false;
    emit signal_exec(query_cmd,params,gen_sig,remote_mode);
    while (!done)
        QApplication::processEvents();

    if (!data_error)
        return true;
    else
        return false;
}

//слот обработки ответа от sql
void QDatabase_Manager::slot_set_done(bool error)
{
    done = true;
    data_error = error;
}


Класс БД
Код:
class QNero_DataBase : public QObject
{
    Q_OBJECT
public:
     ......
signals:

    void signal_set_connected(bool remote,bool value);
    void signal_set_done(bool data_error);
public slots:
    .......
    //подготовить и послать команду SQL
    bool slot_exec(const QString & query_cmd, QList<QNero_DataBase::QUERY_PARAM_T> * params = NULL,bool gen_sig = true,bool remote_mode = true);
private:
    QSqlDatabase local_db,remote_db;//локальная и удаленная базы данных
    bool remote_transaction;//текущая транзакция к удаленной БД?
    QSqlQuery * local_query,* remote_query;
    QSqlRecord record;
};

//подготовить и послать команду SQL
bool QNero_DataBase::slot_exec(const QString & query_cmd, QList<QNero_DataBase::QUERY_PARAM_T> * params,bool gen_sig,bool remote_mode)
{       
    //подготовка команды

        remote_query -> clear();
        remote_query -> prepare(query_cmd);
        if (params != NULL)
            for (int i = 0; i < params -> count(); i++)
                remote_query -> bindValue(params -> at(i).key,params -> at(i).value);

    //отправка
        if (!remote_query -> exec())
        {           
            emit signal_set_done(true);//отправляем сигнал о том, что транзакция с БД завершена  с ошибкой
            return false;
        }
        emit signal_set_done(false);//отправляем сигнал о том, что транзакция с БД завершена
        record = remote_query -> record();

    return true;
}
« Последнее редактирование: Апрель 11, 2013, 11:41 от soiam » Записан
Bepec
Гость
« Ответ #3 : Апрель 11, 2013, 12:00 »

Не понимаю проблемы. Ладно, если много записей, то сигналами лучше не посылать.

Но чем не угодила просто посылка сигнала "всё готово" и вызов функции, защищённой мутексами, возвращающей результаты?

PS прошу простить, если не понял смысла вопроса.
Записан
soiam
Гость
« Ответ #4 : Апрель 11, 2013, 12:12 »

Сразу так и сделал, сделал прямой вызов next() и value()

Код:
//переход к следующей записи, возвращает true, если запись есть
bool QDatabase_Manager::next()
{
    return nero_db -> next();
}


//получение значение колонки column текущей записи
QVariant QDatabase_Manager::value(const QString & column)
{
    return nero_db -> value(column);
}

Код:
//переход к следующей записи, возвращает true, если запись есть
bool QNero_DataBase::next()
{
    return remote_query -> next();
}


//получение значение колонки column текущей записи
QVariant QNero_DataBase::value(const QString & column)
{
    return remote_query -> value(record.indexOf(column));
}
Мютексы пока что не вводил так как операции с БД последовательны по коду
лог Qtcreatora часто начал выдавать QSqlQuery::value: not positioned on a valid record, хотя частично данные читаются из QSqlQuery
Кроме этого программа вываливается на случайной транзакции на строчке return remote_query -> value(record.indexOf(column)); (причем если идти по стеку функций вверх, то этот столбец не первый по счету в извлечении, т.е. до него другие столбцы извлекались нормально).
Подумал, что это из-за того, что я обращаюсь к объекту в другом потоке минуя сигналы-слоты...
Записан
Bepec
Гость
« Ответ #5 : Апрель 11, 2013, 12:55 »

А тупо выдирать записи в список и передавать списком не пробовали?

А то как мне кажется, передавать запрос, чтобы потом тянуть информацию из него несколько затратно.
Записан
soiam
Гость
« Ответ #6 : Апрель 11, 2013, 13:11 »

Решил проблему. не успевал устанавливаться record. После посылки сигнала происходило быстрое переключение на другой  поток, в котором начиналось извлечение данных(отработка слота, что всё хорошо) с помощью next и value. А так как record не был корректно установлен, он хранил значения от предыдущей транзакции.
        emit signal_set_done(false);//отправляем сигнал о том, что транзакция с БД завершена
        record = remote_query -> record();

Соответственно имена столбцов у транзакций не совпадали и программа на remote_query -> value(record.indexOf(column)) падала, так как record.indexOf(column) = -1
Всем спасибо за помощь!
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.051 секунд. Запросов: 21.