Russian Qt Forum

Qt => Базы данных => Тема начата: PinkPanther от Февраль 21, 2015, 16:52



Название: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 21, 2015, 16:52
В классе MainWindow я дописал переменную базы QSqlDatabase db, в конструкторе база определяется и открывается. Там же определяется QSqlQuery query(db).
В других классах иногда требуется обращаться к этой базе. Нужно ли при объявлении новых объектов QSqlQuery задавать базу явным образом, или все они будут обращаться к единственной открытой?


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Johnik от Февраль 21, 2015, 22:35

В классе MainWindow я дописал переменную базы QSqlDatabase db, в конструкторе база определяется и открывается.
В классе MainWindow, лучше вообще не хранить QSqlDatabase, инициализировать, но не хранить.
Нужно ли при объявлении новых объектов QSqlQuery задавать базу явным образом, или все они будут обращаться к единственной открытой?
Если имя при инициализации не задавали, то будет использоваться подключение по умолчанию, и не будет надобности каждый раз указывать QSqlDatabase.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 26, 2015, 05:51
Цитировать
В классе MainWindow, лучше вообще не хранить QSqlDatabase[/qupte]
Согласен...

Цитировать
Если имя при инициализации не задавали, то будет использоваться подключение по умолчанию
У меня это почему-то не работает. Попытка создать еще одну переменную QSqlQuery без задания имени базы приводит к ошибке исполнения запроса этой переменной...
Вероятно, я не определил коннекшн по умолчанию. С этим разберусь, спасибо.

Господа, еще один вопрос:

Можно ли использовать одну и ту же базу разными приложениями параллельно? Приложения находиться в одной папке, база находится там же. У меня как только одно приложение открывает базу, остальные работать с ней не могут.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 26, 2015, 09:00
Да, можно. Даже такие базы как sqlite - указывается таймаут ожидания базы и все работает. А для обычных СУБД так вообще не вопрос.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 26, 2015, 21:25
Да, можно. Даже такие базы как sqlite - указывается таймаут ожидания базы и все работает. А для обычных СУБД так вообще не вопрос.

Спасибо! Если я правильно понял, вы предлагаете открывать базу по требованию, и закрывать в конце работы, отдавая файл другим приложениям. При этом, если база занята, ждать и разумное время крутить цикл событий. Правильно?

А можно ли сделать иначе - открыть базу (файл) для нескольких приложений одновременно? В моем случае есть важная особенность - приложения читают общую информацию, а пишут только в свои личные строки/таблицы. Коллизий возникнуть не может.
Можно ли организовать несколько подключений к одной и той же базе из разных процессов, или вариант доступа с ожиданием самый оптимальный?


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 27, 2015, 09:02
Первое предположение неверное. Я писал именно про второй вариант.
Давайте немного конкретизируем: о какой СУБД мы говорим?


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 28, 2015, 12:18
Первое предположение неверное. Я писал именно про второй вариант.
Давайте немного конкретизируем: о какой СУБД мы говорим?

SQLite 3, стандартная поставка Qt, создается/открывается в 5 строчек кода, создается объект QSqlDatabase, у меня он называется db. Все, что касается открытия, скопировано из хелпов и книги Шлее. После открытия можно создать неограниченное количество QSqlQuery(db), и все они будут иметь доступ к базе.
У меня имеется несколько приложений, которые хотели бы иметь доступ к одной базе, но когда база открыта одним приложением, остальные приложения получить к ней доступ не могут.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 28, 2015, 12:28
Делайте где-то так
Код
C++ (Qt)
   database = QSqlDatabase::addDatabase("QSQLITE");
   database.setConnectOptions("QSQLITE_BUSY_TIMEOUT=5000");
 
Время ожидания освобождения базы - 5 сек. Можно поставить любое.
У меня несколько приложений (в основной массе 2 одновременно, не считая потоков) замечательно работают с одной базой, за 5-6 лет не было еще ни одного случая когда они базу не поделили. И кол-во машин на которых это работает 1000+.
Единственное на операциях которые длятся заведомо дольше чем 5 секунд (массовая заливка данных в базу) я отслеживаю время выполнения запросов и делаю транзакции не более 3-х секунд.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 28, 2015, 12:31
И еще:
При работе с QSqlQueryModel нужно обязательно использовать canFetchMore.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 28, 2015, 17:02
Делайте где-то так
Код
C++ (Qt)
   database = QSqlDatabase::addDatabase("QSQLITE");
   database.setConnectOptions("QSQLITE_BUSY_TIMEOUT=5000");
 
Время ожидания освобождения базы - 5 сек. Можно поставить любое.
У меня несколько приложений (в основной массе 2 одновременно, не считая потоков) замечательно работают с одной базой, за 5-6 лет не было еще ни одного случая когда они базу не поделили. И кол-во машин на которых это работает 1000+.
Единственное на операциях которые длятся заведомо дольше чем 5 секунд (массовая заливка данных в базу) я отслеживаю время выполнения запросов и делаю транзакции не более 3-х секунд.

Спасибо! И если установить этот параметр, ошибки на стадии открытия - лока базы, если она уже открыта одним приложением - возникать не будет? У меня проблема именно в этом. Если база уже открыта, никаких проблем записи-считывания не возникает.

Я хотел решить проблему немного иначе - установить таймаут непосредственно на стадии чтения-записи, встроив его в производный от QSqlQuery класс. Столкнувшись с ошибкой открытия, пришел к мысли, что, если не получается открыть базу одновременно, ее можно открывать по необходимости, а таймаут встроить в метод открытия базы. Открываем, если база залочена - ждем те же 5 секунд (в моем случае, т.к. запросы простые и редкие, должно хватить 2-3 секунд), быстро делаем необходимое и закрываем, освобождая файл базы для других процессов.
К тому же, это сразу избавит от возможных коллизий, когда одно приложение частично изменило таблицу, а второе в этот же момент хочет получить оттуда данные.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 28, 2015, 17:11
Спасибо! И если установить этот параметр, ошибки на стадии открытия - лока базы, если она уже открыта одним приложением - возникать не будет? У меня проблема именно в этом. Если база уже открыта, никаких проблем записи-считывания не возникает.

Кстати про лок. Я работаю под Linux-ом. Может как-то зависит?

Ну, а в целом не должно быть локов. Eсли каждое из приложений будет укладываться со своими запросами в 5 секунд и при работе с моделями из запросов будут выгребаться записи через canFetchMore.
Попутно порекомендовал бы для записи использовать транзакции, а для чтения если перебор идет только в одном направлении для запроса указывать setForwardOnly(true).


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 28, 2015, 19:22
Спасибо! И если установить этот параметр, ошибки на стадии открытия - лока базы, если она уже открыта одним приложением - возникать не будет? У меня проблема именно в этом. Если база уже открыта, никаких проблем записи-считывания не возникает.

Кстати про лок. Я работаю под Linux-ом. Может как-то зависит?

Ну, а в целом не должно быть локов. Eсли каждое из приложений будет укладываться со своими запросами в 5 секунд и при работе с моделями из запросов будут выгребаться записи через canFetchMore.
Попутно порекомендовал бы для записи использовать транзакции, а для чтения если перебор идет только в одном направлении для запроса указывать setForwardOnly(true).

У меня все под Windows.
Лок, вероятно, связан с открытием файла... Приложение открывает базу как обычный файл, на чтение и запись, и другие приложения не могут получить доступ. Другие причины в голову не приходят. Фактически, я и хочу работать в режиме транзакций, т.к. использование базы будет ограничено одним потоком (работа с Model не реализована, только ручные SQL-запросы). Пока работа порции кода не закончена, другие приложения к базе доступ не получают, хотя бы из-за лока. А чтобы избежать коллизий внутри приложения, можно проверять, открыта ли база. Если открыта, ждем, пока закроют, если закрыта - открываем.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 28, 2015, 19:42
Неужели и правда под Windows такое не работает? Вот блин!
А покажите код которым подключаете базу.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 28, 2015, 20:35
Неужели и правда под Windows такое не работает? Вот блин!
А покажите код которым подключаете базу.

Код взят то ли из книжки, или из мануала. Ему года 4, с тех пор ничего менять не требовалось.

Код:
db = QSqlDatabase::addDatabase("QSQLITE");
db.setHostName("myhostname.ru");
db.setDatabaseName("PinkPanther");
db.setUserName("PinkPanther");
db.setPassword("123XYZ321");
if(!db.open())
{
    showCritical("Can't open DATABASE!");
    exit(1);
}
query = QSqlQuery(db);

QSqlDatabase db и QSqlQuery query объявлены в классе главного окна, а код запускается в конструкторе главного окна. В деструкторе база закрывается.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Февраль 28, 2015, 20:55
Подозреваю, что это из-за того как Windows работает с файлами.
Такой подход как я описал, видимо, возможен только или из одного приложения в несколько потоков, или из разных приложений но только под *nix системами.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Февраль 28, 2015, 21:07
Подозреваю, что это из-за того как Windows работает с файлами.
Такой подход как я описал, видимо, возможен только или из одного приложения в несколько потоков, или из разных приложений но только под *nix системами.

Да. Таймаут - это отлично, но сначала необходимо базу открыть, а она, зараза, не открывается.
В старых версиях Windows (3.0, 3.1) была команда share.exe, которая позволяла использовать файл параллельно. Но это было 25 лет назад, с тех пор многое изменилось. С другой стороны, в Windows я могу открыть текстовый файл на редактирование несколькими редакторами одновременно. Но не факт, что они держат его открытым. Скорее всего, качают содержимое, и потом пытаются сохранять в заранее известное место.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Johnik от Март 01, 2015, 01:16
Сделал тестовый проект с двумя подпроектами:
writer - создает бд и пишет данные;
reader - читает из этой бд данные и выводит на экран.

Все работает, проверено на Widnows 7, Qt 5.3.1.

Запускать writer, спустя несколько секунд reader.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Март 01, 2015, 12:45
Получается, что работает таки.
В чем тогда проблема?

PinkPanther: покажите больше кода, может так будет понятна проблема.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Март 01, 2015, 17:11
Johnik, если я правильно понял исходники, whiter запускается, открывает (создает) базу и тестовую таблицу, через 3 секунды пишет в таблицу и отключается. reader открывает базу и читает через 3 секунды работы. Так? Если я запущу reader с интервалом несколько секунд (3+), writer уже умрет, и база будет свободна. Если бы writer писал, потом доживал с открытой базой еще секунд 30, а ридер бы начинал читать через 3 секунды, было бы интересно протестить. Я попробую запустить код со своими задержками, но попозже. Спасибо!

Termit, больше кода, наверное, не нужно. Ошибка вылетает на этапе исполнения того участка, который выложил. Первое запущенное приложение открывает базу без приключений, а следующие спотыкаются и пишут "не могу открыть базу". В других местах db упоминается только в слотах, при создании временных экземпляров QSqlQuery. Я погуглил, гугл говорит, что лок SQLite при попытке доступа из разных приложений - обычная практика. Идеология SQLite - уютненькая базочка для одного приложения. Работать с ней как с сервером БД, видимо, архибессмысленно (при чтении/записи 2,000 записей ждать приходится секунд 10, при этом база живет на SSD-диске).


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Termit от Март 01, 2015, 20:26
У writer-а нет вызова db.close(). База остается открытой.

А больше кода тут уж смотрите сами :)

Что касается области применения sqlite, то да, явно у нее не серверное исполнение. Но никто не заявлял, что она не может работать с несколькими приложениями однозначно.

По делу: порылся в настройках базы, помимо прочего там есть такое понятие как locking_mode - http://sqlite.org/pragma.html#pragma_locking_mode (http://sqlite.org/pragma.html#pragma_locking_mode). Может именно это мешает жить?


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: Johnik от Март 01, 2015, 22:23
whiter запускается, открывает (создает) базу и тестовую таблицу, через 3 секунды пишет в таблицу и отключается.
writer, создает базу и запускает таймер, который через каждые 3 секунды добавляет новую запись в таблице, отключения я не предусматривал. закрыть программу можно только по клавише Ctrl+C

reader, так же каждые три секунды читает последнюю запись из той же таблицы, куда пишет writer, и тоже отключения не предусматривал.


Название: Re: База данных (QSqlDatabase) по умолчанию?
Отправлено: PinkPanther от Март 02, 2015, 03:34
Да, действительно... в обоих примерах база открывается в main() и не закрывается. То есть, при нулевой активности, к базе SQLite можно относительно свободно подключаться (что нормально). Значит, дело в активности первого запущенного приложения.

В ридере-райтере объект QSqlQuery создается в функции, живет в стэке и удаляется при завершении функции, то есть сразу же после чтения или записи. Вероятность, что ридер и райтер пересекутся, минимальна. Гугл говорит, что одна из причин лока - активные query, которые меняли базу, а также активные query, которые использовали курсоры (query.next()). Надо будет провести эксперимент и объявить query не в слоте, а в классе, привязав к базе в конструкторе. Если у ридера возникнут проблемы, значит, дело в этом.