Форум программистов, компьютерный форум, киберфорум
C++ Qt
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.57/7: Рейтинг темы: голосов - 7, средняя оценка - 4.57
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137

Поведение QSqlDatabase

23.02.2018, 17:12. Показов 1732. Ответов 21
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!

Использую класс для работы с БД MySQL. Вот он:

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
class DB
{
public:
    DB();
    ~DB();
 
    bool connect();
    bool auth(QString name, QString pass);
 
private:
    QSqlDatabase db;
};
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
DB::DB()
{
    db = QSqlDatabase::addDatabase("QMYSQL");
}
 
DB::~DB()
{
    qDebug() << "close DB";
 
    db.close();
}
 
bool DB::connect()
{
    qDebug() << "open DB";
 
    db.setHostName("localhost");
    db.setDatabaseName("dbname");
    db.setUserName("user");
    db.setPassword("pass");
 
    return db.open();
}
 
bool DB::auth(QString name, QString pass)
{
    QSqlQuery query(db); // пробовал с параметром и без параметра (одинаково)
 
    query.prepare("SELECT `id` FROM `users` WHERE `name` = ? AND `password` = ?");
 
    query.addBindValue(name);
    query.addBindValue(pass);
 
    query.exec();
 
    return query.next();
}
Создаётся он внутри класса, унаследованного от QRunnable и выполняющегося в каком-то потоке, отличном от основного (я надеюсь, данная информация тут не при чём, но мало ли...).
Программа (в том числе и класс DB) отрабатывает как надо, но не зря же я разместил в коде qDebug. Вот, что мы увидим в консоли, если два раза сынициируем создание потока (в котором создаётся объект DB):

Code
1
2
3
4
5
open DB
close DB
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
open DB
close DB
То есть в первый раз всё нормально, отработал как конструктор, так и деструктор. Но уже во второй раз нам сообщают, что произошло дублирующее подключение, а старое было разорвано.

Программа-то работает, но хочется сделать всё правильно, а если происходят такие возмущения, значит не всё правильно.

Прошу помощи!
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
23.02.2018, 17:12
Ответы с готовыми решениями:

QSqlDatabase странное поведение open()
Заметил одну странность, даже если указать неверные данные для подключения к базе, то программа всё равно говорит об успешном соединении: ...

QSqlDatabase
Привет. Работаю с СУБД sqlite3. Описание конструктора класса, который осуществляет непосредственную работу с БД. ...

QSqlDataBase
Привет! Существует проект (GUI приложение), в котором используется БД SQLite (инклуды: QSqlDataBase, QSqlQuery). Компилится,...

21
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
23.02.2018, 17:36
Ну не делай так. Зачем тебе многократные переподключения?
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
23.02.2018, 17:40
C++ (Qt)
1
    db = QSqlDatabase::addDatabase("QMYSQL", QDateTime::currentDateTime().toString());
nmcf, QSqlDatabase не может в потоки от слова совсем. Либо отдельный поток с базой, либо отдельная база на поток.
0
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
23.02.2018, 18:05
Так при чём здесь потоки? Он использует базу по умолчанию, закрывает, но не удаляет её из списка.
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
23.02.2018, 18:52  [ТС]
Цитата Сообщение от nmcf Посмотреть сообщение
Ну не делай так. Зачем тебе многократные переподключения?
И верно! А как тогда сделать? В моём примере совсем мало кода, не затруднит исправить под свою версию?

Цитата Сообщение от icpu Посмотреть сообщение
db = QSqlDatabase::addDatabase("QMYSQL", QDateTime::currentDateTime().toString()) ;
Правильно ли я понимаю, что после тысячи обращений будет висеть тысяча разных открытых подключений, каждое с ID в виде времени его открытия?
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
23.02.2018, 19:16
Цитата Сообщение от Nell_core Посмотреть сообщение
Правильно ли я понимаю, что после тысячи обращений будет висеть тысяча разных открытых подключений, каждое с ID в виде времени его открытия?
закрытых
0
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
23.02.2018, 19:42
Сколько всего баз у тебя? Если одна, то этот класс вообще не нужен. Перенеси подключение в класс главного окна.
1
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
23.02.2018, 20:53  [ТС]
Цитата Сообщение от nmcf Посмотреть сообщение
Перенеси подключение в класс главного окна
Так и сделал. Всё круто, спасибо.

И всё же. Если по какой-то причине соединение с базой прервётся, что тогда? Я вот думаю, когда я в главном классе произвожу операции подключения:

C++ (Qt)
1
2
3
4
5
6
7
8
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
 
db.setHostName("localhost");
db.setDatabaseName("dbname");
db.setUserName("user");
db.setPassword("pass");
 
db.open();
Могу я соединить сигнал db о потере соединения со слотом реконнекта? Если да, то, скорее всего, изначальный коннект тоже следует выполнить по слоту реконнекта (чтобы не повторять код). Какие ещё сигналы db посоветуешь отслеживать?

И ещё одно. Было бы здорово всю логику работы с БД (подключение, методы работы с данными) хранить в одном классе. В так называемом классе модели. С учётом того, что запросы нужны потокам, возникают сложности. Может быть, передавать указатель на класс модели в конструктор потока? Это, вообще, адекватно? Каковы лучшие практики, как люди обычно организуют код с такими целями?
0
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
23.02.2018, 23:46
А соединение пропадает, что ли? Ты проверял вообще какое будет поведение при этом?
В любом случае должно хватать open().
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
24.02.2018, 09:51  [ТС]
Цитата Сообщение от icpu Посмотреть сообщение
закрытых
Но они как-то фигурируют в памяти приложения?.. Тоже не круто

Добавлено через 1 минуту
Цитата Сообщение от nmcf Посмотреть сообщение
А соединение пропадает, что ли?
Да, пропадает. Через несколько часов работы auth всегда возвращает false

Добавлено через 2 часа 10 минут
Цитата Сообщение от icpu Посмотреть сообщение
db = QSqlDatabase::addDatabase("QMYSQL", QDateTime::currentDateTime().toString()) ;
Всё больше склоняюсь к тому, что это единственно верный путь. Но в конечном счете это решение не верное, ведь в одну и ту же миллисекунду может быть создано два подключения и их ID будет повторяться
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
24.02.2018, 09:55
Цитата Сообщение от Nell_core Посмотреть сообщение
миллисекунду может быть создано два подключения и их ID будет повторяться
Чисто теоретически, да, это возможно. Практически? Вряд ли. Можешь использовать static QAtomicInt поле, это 100% гарантия.
В деструкторе свою базу данных нужно удалять из реестра QSqlDatabase::removeDatabase(db.connectionName());
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
24.02.2018, 10:01  [ТС]
Цитата Сообщение от icpu Посмотреть сообщение
Можешь использовать static QAtomicInt поле, это 100% гарантия
Можешь привести пример кода? Ни разу с таким не сталкивался
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
24.02.2018, 10:47
C++ (Qt)
1
2
3
4
5
6
7
#include <QAtomicInt>
 
DB::DB()
{
    static QAtomicInt counter(0);
    db = QSqlDatabase::addDatabase("QMYSQL", "MyVeryAwesomeConnection_" + QString::number(counter++));
}
1
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
24.02.2018, 11:01
Цитата Сообщение от Nell_core Посмотреть сообщение
Да, пропадает. Через несколько часов работы auth всегда возвращает false
Повторный вызов open() при этом не работает?
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
24.02.2018, 12:55  [ТС]
Цитата Сообщение от nmcf Посмотреть сообщение
Повторный вызов open() при этом не работает?
Хз, он же в конструкторе главного окна)

Добавлено через 36 секунд
Но не это главное. Главное то, что в потоках будет трындец, если так делать

Добавлено через 1 час 47 минут
Цитата Сообщение от icpu Посмотреть сообщение
static QAtomicInt counter(0);
Слушай, вот всё хорошо, но не могу не спросить, а что случится после переполнения QAtomicInt?
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
24.02.2018, 15:39
Уйдёт в минуса, не привлекая лишнего к себе внимания.
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
24.02.2018, 15:39  [ТС]
Ещё один важный нюанс. Нашел на просторах интернета, что такие операции как addDatabase и removeDatabase не являются потокобезопасными.

Получается, что если 2 потока создадут объект класса DB одновременно, то произойдёт нечто нехорошее. Как быть тогда? Может быть создать вот такой мьютекс:
C++ (Qt)
1
Q_GLOBAL_STATIC(QMutex, globalMutex)
И изменить код конструктора:
C++ (Qt)
1
2
3
4
5
6
7
8
DB::DB()
{
    globalMutex.lock();
 
    db = QSqlDatabase::addDatabase("QMYSQL", "MySuperIncredibleConnection_" + QString::number(counter++));
 
    globalMutex.unlock();
}
И деструктора:
C++ (Qt)
1
2
3
4
5
6
7
    globalMutex.lock();
 
    db.close();
 
    QSqlDatabase::removeDatabase(db.connectionName());
 
    globalMutex.unlock();
Нормальное решение?
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
24.02.2018, 15:42
Неплохо, хотя лучше QReadWriteLock

Добавлено через 1 минуту
И делай его статическим полем класса, а не глобальным
0
4 / 4 / 5
Регистрация: 19.08.2016
Сообщений: 137
24.02.2018, 15:44  [ТС]
Цитата Сообщение от icpu Посмотреть сообщение
лучше QReadWriteLock
Чем лучше? Производительность?

У QReadWriteLock вместо одного метода lock методы lockForRead и lockForWrite. В каком случае какой из них использовать?
0
 Аватар для icpu
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
24.02.2018, 15:50
На database вешаешь чтение, на add и remove - запись. Вешать нужно совсем глобально, то есть заменять QSqlDatabase.
Но всё это преждевременные скачки. Ты же не собираешься каждые 0.5 секунд открывать и закрывать соединение? Думаю, нет. А если собираешься, тогда стоит задуматься на переопределении QThreadPool'а, чтобы соединения были привязаны к своим потокам, а не к запускаемым классам.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
24.02.2018, 15:50
Помогаю со студенческими работами здесь

QSqlDatabase
Привет. Есть организационный вопрос по кутэшным базам... У данного класса имеется возможность подключиться к базе с логин-паролем. ...

QSqlDataBase
Имеется таблица. Таблицу мне нужно перенести в БД и почистить. Имеется кусман кода, выполняющий эти действия QTableWidget *tab =...

QSqlDatabase
День добрый. Работаю с БД. Следующим образом создаю (и/или открываю) файл БД: if (!QSqlDatabase::contains()) { b =...

QSqlDatabase::addDatabase
Добрый день. Опять я. Теперь такой вопрос: Я не совсем пока понял принцип работы с базами данных в Qt. Но у меня такая проблема: Я...

QSqlDatabase не работает
QSqlDatabase sdb; pro file: QT += core gui sql При создании sdb ловлю ошибку линкера, unresolved external symbol. В чем проблема? ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! */ #include <iostream> #include <stack> #include <cctype>. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru