Qt и SQLite и вообще, программирование БД в Qt
Ниже пойдет речь о том, как использовать SQLite в Qt. Автор постарался как можно подробнее рассматривать программирование баз данных в Qt.
Об этих двух замечательных продуктах можно прочитать следуя приведенным выше ссылкам, а мы будем конкретно рассматривать программирование БД в Qt, в частности, на примере SQLite. Скажу только, что SQLite несколько отличается от «обычных» баз данных, таких как MySQL тем, что «не обладает» клиент-серверной архитектурой. То есть движок БД не является отдельно работающим процессом, с которым взаимодействует программа. SQLite представляет собой библиотеку, с которой компонуется ваша программа и, таким образом, движок становится составной частью программы. То есть представьте вы решили сохранять все данные, с которыми «сталкивается» ваша программа в обычный файл. В один прекрасный день вы решаете сохранять данные в файле, но организовав это с «реляционной» точки зрения. После этого вы поняли, что новая структура файла должна «распознаваться особым образом». С этого, как минимум, следует, что вам нужно предоставить некоторый API, обеспечивающий связь между этим файлом данных с приложением. В общем, следуя логической постановке приведенного сценария у вас рождается система БД, не требующая сервера БД и собственно, клиента. Получается достаточно быстрая по сравнению с «клиент-серверной» БД система, и сама программа упрощается. Я состою в дружеских отношениях с Qt и недавно мне понадобилось ее БД функциональность. С MySQL я тоже в достаточно дружеских отношениях и попытался использовать Qt с MySQL в программе, в то время разрабатываемой мной. Имея нехватку времени и нервов, чтоб «связать» MySQL с Qt, решил воспользоваться SQLite, для чего в Qt есть, так сказать, встроенная поддержка, то есть ничего нового установить/конфигурировать не надо было (это не относиться к случаю, если ваш Qt собран с поддержкой «считанных» модулей, без подключения модуля QtSql). И еще, если мне придется установить программу в другой компьютер, я не буду «вынужден» установить сервер MySQL и т.д. (спорная тема — знаю).
На данный момент я использую программу SQLiteManager для создания БД, таблиц и т.д., использую недавно, но программа сразу понравилась. В моей «рабочей лошадке» установлен(a?) Qt Windows SDK и я использую QtCreator, сразу скажу — просто блеск (не ИМХО, и вправду отличная IDE).
И так, Qt и базы данныхКак уже неявно упомянулось выше, в Qt есть отдельный модуль, предоставляющий удобный «сервис» использования БД — QtSql. Если у вас есть опыт работы с Qt, то о файле .pro вам известно, если нет — познакомьтесь. Помните только, что нужно добавить следующую строку в .pro файл:
Это, чтоб использовать модуль QtSql, а для работы с ее классами, нужно включать одноименный заголовок.
- Уровень драйверов
- Программный уровень
- Уровень пользовательского интерфейса
- QSqlDriver
- QSqlDriverCreator<T*>
- QSqlDriverCreatorBase,
- QSqlDriverPlugin
- QSqlResult
Для соединения с базой данных прежде всего нужно активизировать драйвер используя статический метод QSqlDatabase::addDatabase(). Метод получает строку как аргумент, обозначающий идентификатор драйвер СУБД. Нам понадобится «QSQLITE».
У статической функции addDatabase есть перегруженный «брат», получающий не имя драйвера, а сам драйвер(QSqlDriver*). Соединение осуществляется методом open(). Класс QSqlDatabase представляет соединение с БД. Соединение предоставляет доступ к БД через поддерживаемый драйвер БД. Важно, что можно иметь несколько соединений к одной БД. Если при соединении (метод open()) возникла ошибка, то получить информацию об ошибке можно через метод QSqlDatabase::lastError() (возвращает QSqlError).
Рассмотрим как Qt позволяет исполнять команды SQL. Для этого можно воспользоваться классом QSqlQuery. Класс может быть использована не только для исполнения DML (Data Manipulation Language) выражений, таких, как SELECT, INSERT, UPDATE и DELETE, но и DDL (Data Definition Language) выражений, таких, как CREATE TABLE. Обратите внимание, что может быть выполнена и БД-специфичная команда, не ялвяющийся стандартом SQL (например, для PSQL — «SET DATESTYLE=ISO»). Удачно выполненные запросы устанавливают состояние запроса в «активный», так, isActive() возвратит true, в противоположном случае состояние устанавливается в неактивное. Запросы оформляются в виде обычной строки, которая передается в конструктор или в метод QSqlQuery::exec(). В первом случае, при передаче конструктору, запуск команды будет производиться автоматически (при конструировании объекта). Что очень интересно, так это возможность навигации, предоставляемый QSqlQuery. Например, после запроса SELECT можно перемещаться по собранным данным при помощи методов next(), previous(), first(), last() и seek().
Метод next() позволяет перемащатся на следующую строку данных, а вызов previous() на предыдущую строку, соответственно. first(), last() извлекают, соответственно, первую запись из результата. seek() получает целочисленный индекс, извлекая запись из результата по полученному индексу и «позиционирует запрос» на извлеченную запись. Проверить размер, вернее количество строк данных (результата) можно методом size(). Важно помнить, что первая запись находится в позиции 0, запрос должен быть в активном состоянии, а isSelect() возвращать true (это происходит, если последним запросом был SELECT) перед вызовом метода seek(). О методе seek() более подробно советую прочитать в официальной документации. Выше упомянули, что если передавать строку запроса в конструктор класса QSqlQuery, то запрос выполнится при создании объекта — при конструировании. Используя метод exec() можно, так сказать, следить за временем выполнения запросов. Конструкция может быть представлена также так: Так, exec() получает запрос в виде QString. Выполняя запрос, в случае удачи этот метод возвращает true и устанавливает состояние в активное, в противоположном случае все «противоположное» указанным операциям. Конечно, следует еще и помнить, что строка запроса должна подчиняться синтаксическим правилам запрашиваемой БД (в частности, стандарту SQL). Что интересно, так после исполнения, запрос позиционируется на инвалидный(ую?) запись, то есть для адекватного использования результатов, необходимо воспользоваться, скажем, методом next(). У метода exec() перегруженная альтернатива, не получающая никаких аргументов. Вызов этого варианта exec() исполняет до этого подготовленный запрос. Обратите внимание — «подготовленный». Для этого предназначен метод prepare(), который возвращает true в случае удачной подготовки запроса. Важность или, можно сказать, уникальность метода в том, что запрос может содержать «заполнители» для связывания со значениями используая bindValue().
Еще можно использовать вариант безымянных параметров:
И наконец, можно просто использовать подставляемые аргументы, которые предоставляет QString:
Компилируемый (copy-paste-to-your-ide) пример:
Для получения результата запроса следует вызвать метод QSqlQuery::value(), в котором необходимо передать номер столбца, для чего в примере воспользовались методом record(). Этот метод возвращает объект класса QSqlRecord, который содержит информацию, относящуюся к запросу SELECT. С помощью вызова QSqlRecord::indexOf() получаем индекс столбца. Метод value() возвращает значения типа QVariant (класс, объекты которого могут содержать в себе значения разных типов), поэтому вы и преобразовали полученное значение, воспользовавшись методами QVariant::toInt() и QVariant::toString().
Уровень пользовательского интерфейсаМодуль QtSql поддерживает концепцию «Интервью», предоставляя ряд моделей для использования их в представлениях. Чтобы хорошенько познакомиться с этой концепцией — загляните сюда. В качестве примера, класс QSqlTableModel позволяет отображать данные в табличной и иерархической форме. Как утверждается в литературе, интервью — самый простой способ отобразить данные таблицы, здесь не потребуется цикла для прохождения по строкам таблицы. Вот малюсенький пример: