Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Qt и кроссплатформенное программирование С/С++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 08.07.2017, 17:15   #1
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию Неправильно работает bindValue перед выполнением запроса

Я пишу шаблон функции для извлечения заданных полей из таблицы БД. Код такой:

Код:
template <typename ReturnType>
ReturnType get(const QString &field, int id)
{
    QSqlQuery query;
    QString sql = "SELECT :field FROM employee WHERE id = :id";

    query.prepare(sql);
    query.bindValue(":field", QVariant(field));
    query.bindValue(":id",     QVariant(id));

    query.exec();
    query.next();

    qDebug() << query.value(0).toString();

    return qvariant_cast<ReturnType>(query.value(0));
}
У меня возникла проблема, видимо, с привязкой значений параметров при помощи bindValue. Когда запрос выполняется, возвращается не значение из поля таблицы, а название колонки, например, "firstname", если я перед этим вызывал bindValue(":field", "firstname").

Во всех примерах показано именно такое использования привязки. Что я делаю не так?
Aoizora вне форума Ответить с цитированием
Старый 08.07.2017, 20:55   #2
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Сделайте для начала проверку всех возвращаемых значений функциями на предмет ошибок, м.б. запрос вообще не исполнен, например, не было связи.



Ну развейте мысль

Код:
template <typename ReturnType, class PT>
ReturnType get(const QString &field, PT param);
можно еще развить, через шаблоны из <type_traits> проверять, если PT не простой тип - брать на него ссылку (чтоб классы не копировать).
При вызове, указывать можно только ReturnType, второй компилер из аргумента выведет сам.

...хотя вроде глупость написал Вам же для WHERE это надо будет. Ну пусть останется, в качестве идеи, что параметры тож можно в шаблон )

Последний раз редактировалось alexzk; 08.07.2017 в 21:18.
alexzk вне форума Ответить с цитированием
Старый 08.07.2017, 21:39   #3
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

...вообще, в таком виде будет сильно тормознуто и не эффективно. Будет гуй вешатся и т.д.

Я делал так - поток, он открывает БД и по окончании закрывает, как локальную переменную, т.е. больше ниоткуда к БД никак.
Дальше поток проверяет очередь из
struct
{
QSqlQuery a;
std::function<> res;
}

если есть чо - исполняет запрос, вызывает ф.-ответ (все это еще меж поточно синхр. нада и учитывать, сам запрос к БД нет, а вот очереди и ответы - да).

тогда ваш гет бы создавал QSqlQuery, и ложил его в очередь потока, с ответом - тут нада смареть, какая синронизация сделана.
alexzk вне форума Ответить с цитированием
Старый 08.07.2017, 22:06   #4
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
...вообще, в таком виде будет сильно тормознуто и не эффективно. Будет гуй вешатся и т.д.

Я делал так - поток, он открывает БД и по окончании закрывает, как локальную переменную, т.е. больше ниоткуда к БД никак.
Дальше поток проверяет очередь из
struct
{
QSqlQuery a;
std::function<> res;
}

если есть чо - исполняет запрос, вызывает ф.-ответ (все это еще меж поточно синхр. нада и учитывать, сам запрос к БД нет, а вот очереди и ответы - да).

тогда ваш гет бы создавал QSqlQuery, и ложил его в очередь потока, с ответом - тут нада смареть, какая синронизация сделана.
Угу, а еще в этот поток приходит DROP TABLE от пользователя. Хотя удобно, да.
Aoizora вне форума Ответить с цитированием
Старый 08.07.2017, 22:29   #5
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от Aoizora Посмотреть сообщение
Угу, а еще в этот поток приходит DROP TABLE от пользователя. Хотя удобно, да.
Дело в том, что как я помню, доступ к БД должен быть из того же потока, что ее и создавал. Там в мелочах каких-то была проблема. Т.е. при многопоточности, оно будет работать, но не всегда( или слишком частые мутексы). Поэтому я так и делал - все в отдельном, чтоб наверняка и гуй не вис.

Последний раз редактировалось alexzk; 08.07.2017 в 22:32.
alexzk вне форума Ответить с цитированием
Старый 08.07.2017, 23:01   #6
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию

Код:
{
QSqlQuery a;
std::function<> res;
}
Похоже на пересылку системных сообщений. А как реализовать возврат значений из потока?
Aoizora вне форума Ответить с цитированием
Старый 08.07.2017, 23:10   #7
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

у меня в итоге было несколько "геттеров" - каждый со своим запросом, вызыватся они могут из разных потоков (т.е. кто-то из гуи, кто-то из расчетников), поэтому у меня тупо сработало ожидать ответ.
Т.е. в структуре еще поле "ожидатора" - promises/features/conditional_variable, передаю в структуру лямбду, которую вызовет поток БД, эта лямбда должна заполнить ответом локальную переменную в геттере, потом геттер уходит в ожидание, сигнал "хватит ждать" можно или в БД прописать, или прям в лямбде (т.к. она в контексте потока БД).
Дождались сигнал - вернули локальную переменную (кстати, луче делать shared_ptr на что-то, в лямбду брать его копию, и заполнять уже указываемую память ответом, так надежнее, если забыть "ожидание", все равно не упадет + не зависит от наличия у потоков своего стека, а это спорный сильно вопрос, MacOS делает его реально маленьким, по нынешним временам).

Но это вобщем еще медленнее будет, если вызовы геттера все из 1 потока типа гуя.

Конкретно для гуя, можно делать через сигнал-слот с последним параметром коннекта Qt::QueuedConnection. Т.о. слот будет всегда вызван в контексте потока, хозяина объекта.
Т.е. лямбда в гетере вызывает сигнал с ответом. Кстати, promise/feature это и есть ожидание типа "сигнал-слот", но для получения в ГУЙ не пойдет - гуй будет висеть.

Последний раз редактировалось alexzk; 08.07.2017 в 23:24.
alexzk вне форума Ответить с цитированием
Старый 09.07.2017, 17:14   #8
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
у меня в итоге было несколько "геттеров" - каждый со своим запросом, вызыватся они могут из разных потоков (т.е. кто-то из гуи, кто-то из расчетников), поэтому у меня тупо сработало ожидать ответ.
Т.е. в структуре еще поле "ожидатора" - promises/features/conditional_variable, передаю в структуру лямбду, которую вызовет поток БД, эта лямбда должна заполнить ответом локальную переменную в геттере, потом геттер уходит в ожидание, сигнал "хватит ждать" можно или в БД прописать, или прям в лямбде (т.к. она в контексте потока БД).
Дождались сигнал - вернули локальную переменную (кстати, луче делать shared_ptr на что-то, в лямбду брать его копию, и заполнять уже указываемую память ответом, так надежнее, если забыть "ожидание", все равно не упадет + не зависит от наличия у потоков своего стека, а это спорный сильно вопрос, MacOS делает его реально маленьким, по нынешним временам).

Но это вобщем еще медленнее будет, если вызовы геттера все из 1 потока типа гуя.

Конкретно для гуя, можно делать через сигнал-слот с последним параметром коннекта Qt::QueuedConnection. Т.о. слот будет всегда вызван в контексте потока, хозяина объекта.
Т.е. лямбда в гетере вызывает сигнал с ответом. Кстати, promise/feature это и есть ожидание типа "сигнал-слот", но для получения в ГУЙ не пойдет - гуй будет висеть.
Ты как-то бессистемно излагаешь мысли и не выделяешь главное.
Для возврата результата выполнения запроса из потока ты вызываешь лямбду, в которой локальная переменная геттера захвачена по ссылке. Для чего в структуре QSqlQuery? Почему не передавать просто запрос в QString? Можно ли использовать само поле QSqlQuery для возвращения данных после выполнения запроса? Ведь внутри будут храниться полученные данные.
Для возврата значений при помощи вызова лямбды по ссылке захватывается QVariant, QSqlQuery или переменная какого-то другого типа?

Все это больше похоже на очень плохой дизайн с кучей сущностей, высокой связностью и запутанностью. И надо помнить, где, например, соединение открыто, а где еще нет.

Код:
void QueryHandler::run()
{
    DBConnection::connect();

    while (true)
    {
        if (!sql_queue.empty())
        {
            auto msg = sql_queue.dequeue();
            msg.query.exec();
            msg.result(0);
        }
        else msleep(10);
    }
}

Последний раз редактировалось Aoizora; 09.07.2017 в 17:45.
Aoizora вне форума Ответить с цитированием
Старый 09.07.2017, 20:37   #9
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Умолчательное соединение - вообще плохая идея, оно отовсюду доступно и 2 БД не открыть этим же классом.

Сделайте лучше - я только идей набросал, полный код там порядком.
...ксати да, прогнал - передается строка, который создает Query объект у меня, спутал с QSQlQuery, который действительно в цикле

Код:
while (!stopThread && !globExit())
    {
        bool empty;
        {
            std::unique_lock<std::mutex> lck(query_mut);
            empty = query.empty();
        }
        if (empty)
            std::this_thread::sleep_for(std::chrono::milliseconds(150));
        else
        {
            QueryPtr q(nullptr);
            {
                std::unique_lock<std::mutex> lck(query_mut);
                q = query.front();
                query.pop();
            }
            if (q)
            {
                bool res_succ = false;
                try
                {
                    if (!stopThread && !globExit())
                    {
                        std::shared_ptr<QSqlQuery> sql(new QSqlQuery(database));
                        if (sql)
                        {
                            if (sql->exec(q->sql))
                            {
                                res_succ = true;
                                if (!stopThread && !globExit())
                                    q->result(*sql);
                                sql->clear();
                            }
                            else
                            {
                                qWarning() << "SqlDB error: " << sql->lastError().text()<<"\nQuery: "<<q->sql;
                            }
                        }
                    }
                }
                catch(...)
                {
                    res_succ = false;
                    qWarning() << "Exception into DB thread";
                }

                q->confirm(res_succ);
            }
        };
    }
а вот выделения через new, это остатки "как я искал баг" - были странные падения на маке, иногда. Вообщем выяснил, что стек у потоков на маке 512кб. Когда не хватает его - начинается падучесть.

Последний раз редактировалось alexzk; 09.07.2017 в 20:45.
alexzk вне форума Ответить с цитированием
Старый 10.07.2017, 15:26   #10
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию

Как ты запускал поток, проверяющий очередь SQL-запросов? Наследовал класс собственного потока от QThread или использовал std::thread, у которого вызывал detach в функции init(), которая при запуске программи инициализирует все необходимые ресурсы?
Aoizora вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Неправильно выводятся стили из-за PHP запроса Alex2213 PHP 1 18.07.2015 21:32
Программа работает неправильно. Настюня Помощь студентам 2 16.06.2012 18:34
Проблема с выполнением запроса к БД AgentSmit PHP 2 14.04.2011 02:13
Неправильно работает WinExec? TwiX Общие вопросы Delphi 2 26.11.2009 21:07
"подвисание" перед выполнением макроса Gavr Microsoft Office Excel 1 09.08.2009 15:31