[Предыдущая: Модуль QtScript] [Модули Qt] [Следующая: Модуль QtSvg] Модуль QtSql
|
QSql | Содержит различные идентификаторы, используемые по всей библиотеке SQL Qt |
---|
QSqlDatabase | Предоставляет соединение с базой данных |
---|---|
QSqlDriver | Абстрактный базовый класс для доступа к специфическим базам данных SQL |
QSqlDriverCreator | Класс-шаблон, предоставляющий фабрику драйверов SQL для специфических типов драйверов |
QSqlDriverCreatorBase | Базовый класс для фабрик драйверов SQL |
QSqlDriverPlugin | Абстрактный базовый класс для пользовательских подключаемых модулей QSqlDriver |
QSqlError | Информация об ошибке базы данных SQL |
QSqlField | Управление полями в таблицах и представлениях базы данных SQL |
QSqlIndex | Функции для управления индексами базы данных и их описания |
QSqlQuery | Средства управления выражениями SQL и их выполнения |
QSqlQueryModel | Модель данных только для чтения результирующей SQL-выборки |
QSqlRecord | Заключает в себе запись базы данных |
QSqlRelation | Содержит информацию о внешнем ключе SQL |
QSqlRelationalDelegate | Делегат, используемый для отображения и редактирования данных из QSqlRelationalTableModel |
QSqlRelationalTableModel | Модель данных для редактирования одной таблицы базы данных с поддержкой внешних ключей |
QSqlResult | Абстрактный интерфейс для доступа к данным специфичной базы SQL |
QSqlTableModel | Редактируемая модель данных для одной таблицы базы данных |
Классы SQL подразделяются на три слоя:
Слой | Описание |
---|---|
Драйвер слоя | Включает классы QSqlDriver, QSqlDriverCreator<T>, QSqlDriverCreatorBase, QSqlDriverPlugin и QSqlResult. Этот слой предоставляет низкоуровневый мост между определенными базами данных и слоем SQL API. Для получения более подробной информации смотрите Драйвера баз данных SQL. |
SQL API Layer | Эти классы предоставляют доступ к базам данных. Соединения устанавливаются с помощью класса QSqlDatabase. Взаимодействие с базой данных осуществляется с помощью класса QSqlQuery. В дополнение к классам QSqlDatabase и QSqlQuery слой SQL API опирается на классы QSqlError, QSqlField, QSqlIndex и QSqlRecord. |
Слой пользовательского интерфейса | Эти классы связывают данные из базы данных с дата-ориентированными виджетами. Сюда входят такие классы, как QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel. Эти классы разработаны для работы с каркасом Qt модель/представление. |
Помните, что до использования любого из этих классов должен быть инициализирован объект класса QCoreApplication. Для включения определений классов этого модуля используйте следующую директиву:
#include <QtSql>
Для линковки приложения с этим модулем, добавьте в ваш qmake файл проекта .pro:
QT += sql
Модуль QtSql является частью Выпуска Qt Full Framework и Версий Open Source Qt.
Данный обзор предполагает, что вы имеете, по крайней мере, базовые знания об SQL. Вы должны понимать простые выражения SELECT, INSERT, UPDATE и DELETE. Несмотря на то, что класс QSqlTableModel предоставляет интерфейс для просмотра и редактирования базы данных без знания SQL, наличие базовых представлений об SQL настоятельно рекомендуется. Стандартное описание баз данных SQL представлено в An Introduction to Database Systems (7th Ed.) by C. J. Date, ISBN 0201385902.
Темы:
Чтобы получить доступ к базе данных с помощью QSqlQuery и QSqlQueryModel, создайте и откройте одно или более соединений с базой данных. Соединения с базой данных обычно идентифицируется по имени соединения, а не по имени базы данных. Вы можете иметь множество соединений с одной и той же базой данных. QSqlDatabase также поддерживает концепцию соединения по умолчанию, которое является неименованным. Когда вызываются функции-члены QSqlQuery или QSqlQueryModel, которые получают имя соединения как аргумент, то если вы не указываете имя соединения, будет использоваться соединение по умолчанию. Создание соединения по умолчанию удобно, когда вашему приложению требуется только одно соединение с базой данных.
Обратите внимание на отличие между созданием соединения и его открытием. Создание соединения включает в себя создание экземпляра класса QSqlDatabase. Соединение не пригодно к использованию до тех пор, пока оно не будет открыто. Следующий фрагмент кода показывает, как создать соединение по умолчанию и затем открыть его:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("bigblue"); db.setDatabaseName("flightdb"); db.setUserName("acarlson"); db.setPassword("1uTbSbAs"); bool ok = db.open();
Первая строка создает объект соединения, а последняя открывает его. В промежутке, мы инициализировали некоторую информацию о соединении, включая имя базы данных, имя узла, имя пользователя, пароль. В этом примере, мы соединялись с базой данных MySQL flightdb на узле bigblue. Аргумент "QMYSQL" в addDatabase() тип драйвера базы данных, чтобы использовать для соединения. Набор драйверов баз данных включенных в Qt показан в таблице поддерживаемые драйверы баз данных.
Соединение в примере будет соединением по умолчанию, потому что мы не передаем второй аргумент в addDatabase(), который является именем соединения. Например, здесь мы устанавливаем два соединения с базой данных MySQL называемых "first" и "second":
QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first"); QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second");
После этого соединения инициализированы, open(), для каждого из них, устанавливает активность соединений. Если open() потерпит неудачу, он вернет false. В этом случае, вызовите QSqlDatabase::lastError(), чтобы получить информацию об ошибке.
Как только соединение установлено, мы можем вызвать статическую функцию QSqlDatabase::database(), из любого места программы с указанием имени соединения, чтобы получить указатель на это соединение. Если мы не передаем имя соединения, она вернет соединение по умолчанию. Например:
QSqlDatabase defaultDB = QSqlDatabase::database(); QSqlDatabase firstDB = QSqlDatabase::database("first"); QSqlDatabase secondDB = QSqlDatabase::database("second");
Для удаления соединения с базой данных, сначала закройте базу данных с помощью QSqlDatabase::close(), а затем, удалите ее с помощью статического метода QSqlDatabase::removeDatabase().
Класс QSqlQuery обеспечивает интерфейс для выполнения SQL запросов и навигации по результирующей выборке.
Классы QSqlQueryModel и QSqlTableModel, описанные в следующем разделе, предоставляют высокоуровневый интерфейс для доступа к базам данных. Если вы не знакомы с SQL, вам, возможно, захочется сразу перейти к следующему разделу (Использование классов SQL модели).
Для выполнения SQL запросов, просто создают объект QSqlQuery и вызывают QSqlQuery::exec(). Например, вот так:
QSqlQuery query; query.exec("SELECT name, salary FROM employee WHERE salary > 50000");
Конструктор QSqlQuery принимает необязательный аргумент QSqlDatabase, который указывает, какое соединение с базой данных используется. В нижеприведенном примере мы не указываем соединение, поэтому используется соединение по умолчанию.
Если возникает ошибка, exec() возвращает false. Доступ к ошибке можно получить с помощью QSqlQuery::lastError().
QSqlQuery предоставляет единовременный доступ к результирующей выборке одного запроса. После вызова exec(), внутренний указатель QSqlQuery указывает на позицию перед первой записью. Мы должны вызвать метод QSqlQuery::next() один раз, чтобы переместить указатель к первой записи, затем снова повторять вызов next(), чтобы получать доступ к другим записям, до тех пор пока он не вернет false. Вот типичный цикл, перебирающий все записи по порядку:
while (query.next()) { QString name = query.value(0).toString(); int salary = query.value(1).toInt(); qDebug() << name << salary; }
Функция QSqlQuery::value() возвращает значение поля текущей записи. Поля задаются индексами, начиная с нуля. Функция QSqlQuery::value() возвращает значение типа QVariant, который может хранить значения различных типов C++ и ядра Qt, такие как int, QString и QByteArray. Различные типы значений базы данных автоматически приводятся к ближайшему эквиваленту в Qt. В данном фрагменте кода мы вызываем QVariant::toString() и QVariant::toInt() для преобразования переменных в QString и int.
For an overview of the recommended types used with Qt supported Databases, please refer to this table.
Вы можете перемещаться взад и вперед по выборке, используя функции QSqlQuery::next(), QSqlQuery::previous(), QSqlQuery::first(), QSqlQuery::last() и QSqlQuery::seek(). Текущий номер строки можно получить с помощью QSqlQuery::at(), а общее количество строк в выборке, если это поддерживается базой данных, возвращается функцией QSqlQuery::size().
Определить, поддерживает ли драйвер базы данных определенную особенность можно с помощью вызова функции QSqlDriver::hasFeature(). В следующем примере мы вызываем QSqlQuery::size() для определения размера результирующей выборке, только в том случае, если база данных поддерживает такую возможность; в противном случае мы перемещаемся к последней записи и используем ее позицию в выборке для определения количества записей.
QSqlQuery query;
int numRows;
query.exec("SELECT name, salary FROM employee WHERE salary > 50000");
QSqlDatabase defaultDB = QSqlDatabase::database();
if (defaultDB.driver()->hasFeature(QSqlDriver::QuerySize)) {
numRows = query.size();
} else {
// это может быть очень медленно
query.last();
numRows = query.at() + 1;
}
Если вы перебираете результирующую выборку только с помощью вызовов next() и seek() с положительными значениями, то можете перед вызовом exec() вызвать QSqlQuery::setForwardOnly(true). Эта небольшая оптимизация сильно ускорит выполнение запросов, возвращающих большие выборки.
QSqlQuery может выполнять любые SQL выражения, а не просто SELECT'ы. Следующий пример вставляет запись в таблицу, используя INSERT:
QSqlQuery query; query.exec("INSERT INTO employee (id, name, salary) " "VALUES (1001, 'Thad Beaumont', 65000)");
Если вы хотите одновременно вставить множество записей, то зачастую эффективней отделить запрос от реально вставляемых значений. Это можно сделать с помощью вставки значений через параметры. Qt поддерживает два синтаксиса вставки значений: поименованные параметры и позиционные параметры. В следующем примере показана вставка с помощью поименованного параметра:
QSqlQuery query; query.prepare("INSERT INTO employee (id, name, salary) " "VALUES (:id, :name, :salary)"); query.bindValue(":id", 1001); query.bindValue(":name", "Thad Beaumont"); query.bindValue(":salary", 65000); query.exec();
В этом примере показана вставка с помощью позиционного параметра:
QSqlQuery query; query.prepare("INSERT INTO employee (id, name, salary) " "VALUES (?, ?, ?)"); query.addBindValue(1001); query.addBindValue("Thad Beaumont"); query.addBindValue(65000); query.exec();
Оба синтаксиса работают со всеми драйверами баз данных предоставляемыми Qt. Если база данных поддерживает синтаксис, Qt просто пересылает запрос в СУБД; в противном случае, Qt симулирует синтаксис параметров и осуществляет предобработку запроса. Фактический запрос, который поступает на выполнение в СУБД доступен с помощью QSqlQuery::executedQuery().
При вставке множества записей, вам требуется вызвать QSqlQuery::prepare() только однажды. Далее вы можете вызвать bindValue() или addBindValue() с последующим вызовом exec() столько раз, сколько потребуется.
Помимо удобства выполнения, вставка через параметры имеет еще и то преимущество, что вы избавлены от необходимости заботиться о преобразовании специальных символов.
Изменение записей очень похоже на вставку в таблицу:
QSqlQuery query; query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003");
Вы также можете использовать поименованную или позиционную вставку для ассоциирования параметров строки запроса с актуальными значениями.
В конце приведем пример выражения DELETE:
QSqlQuery query; query.exec("DELETE FROM employee WHERE id = 1007");
Если основной движок базы данных поддерживает транзакции, то QSqlDriver::hasFeature(QSqlDriver::Transactions) возвратит true. Для инициации транзакции вы можете использовать QSqlDatabase::transaction(), затем запустить инструкции SQL, которые вы хотите выполнить в пределах транзакции, а после вызвать QSqlDatabase::commit() или QSqlDatabase::rollback(). При использовании транзакции вы должны начать её, прежде чем создадите свой запрос.
Пример:
QSqlDatabase::database().transaction(); QSqlQuery query; query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'"); if (query.next()) { int employeeId = query.value(0).toInt(); query.exec("INSERT INTO project (id, name, ownerid) " "VALUES (201, 'Manhattan Project', " + QString::number(employeeId) + ')'); } QSqlDatabase::database().commit();
Транзакции можно использовать для того, чтобы гарантировать атомарность сложного действия (например, просмотр внешних ключей и создание записи) или для возможности отмены сложного действия в процессе его выполнения.
В дополнение к QSqlQuery Qt предлагает три высокоуровневых класса для работы с базами данных. Это классы QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel.
QSqlQueryModel | Модель только-для-чтения, основанная на произвольных SQL-запросах. |
QSqlTableModel | Модель для чтения-записи работающая с одной таблицей. |
QSqlRelationalTableModel | Подкласс QSqlTableModel с поддержкой внешних ключей. |
Эти классы происходят от QAbstractTableModel (который происходит от QAbstractItemModel) и могут существенно облегчить представление данных из базы данных в элементно-ориентированных классах таких, как QListView и QTableView. Это подробно объясняется в разделе Отображение данных в таблице-представлении.
Другое преимущество использования этих классов состоит в том, что они облегчают приспособление кода к другому источнику данных. Например, если вы использовали QSqlTableModel, а затем решили вместо базы данных использовать XML-файлы для хранения данных, то изменение кода - это вопрос замены одной модели данных на другую.
QSqlQueryModel предлагает основанную на SQL запросе модель только-для-чтения.
Пример:
QSqlQueryModel model; model.setQuery("SELECT * FROM employee"); for (int i = 0; i < model.rowCount(); ++i) { int id = model.record(i).value("id").toInt(); QString name = model.record(i).value("name").toString(); qDebug() << id << name; }
После настройки запроса с помощью QSqlQueryModel::setQuery(), вы можете использовать QSqlQueryModel::record(int) для получения доступа к отдельным записям. Также можете использовать QSqlQueryModel::data() и другие функции, унаследованные от QAbstractItemModel.
Также есть перегруженный метод setQuery(), который принимает объект QSqlQuery и работает с его результирующей выборкой. Это позволяет вам использовать любые возможности QSqlQuery для установки запроса (например, подготовленные запросы QSqlQuery::prepare()).
QSqlTableModel предлагает модель для чтения и записи, которая работает одновременно только с одной таблицей SQL.
Пример:
QSqlTableModel model; model.setTable("employee"); model.setFilter("salary > 50000"); model.setSort(2, Qt::DescendingOrder); model.select(); for (int i = 0; i < model.rowCount(); ++i) { QString name = model.record(i).value("name").toString(); int salary = model.record(i).value("salary").toInt(); qDebug() << name << salary; }
QSqlTableModel - это высокоуровневая альтернатива QSqlQuery для изменения отдельной SQL таблицы и навигации по ней. Обычно это требует меньше кода и не требует знания SQL синтаксиса.
Для получения строки таблицы используйте QSqlTableModel::record(), а для ее изменения - QSqlTableModel::setRecord(). Например, следующий код увеличивает зарплату всех сотрудников на 10 процентов:
for (int i = 0; i < model.rowCount(); ++i) { QSqlRecord record = model.record(i); double salary = record.value("salary").toInt(); salary *= 1.1; record.setValue("salary", salary); model.setRecord(i, record); } model.submitAll();
Для доступа к данным вы можете также использовать QSqlTableModel::data() и QSqlTableModel::setData(), которые унаследованы от QAbstractItemModel. В следующем примере показано, как изменить запись с помощью setData():
model.setData(model.index(row, column), 75000); model.submitAll();
Здесь показано, как вставить строку и заполнить ее:
model.insertRows(row, 1); model.setData(model.index(row, 0), 1013); model.setData(model.index(row, 1), "Peter Gordon"); model.setData(model.index(row, 2), 68500); model.submitAll();
Пример удаления пяти следующих друг за другом строк:
model.removeRows(row, 5); model.submitAll();
В качестве первого элемента QSqlTableModel::removeRows() передается индекс первой удаляемой строки.
После окончания редактирования записи, вы должны вызвать QSqlTableModel::submitAll() для того, чтобы гарантировать запись изменений в базу данных.
Когда и необходимо ли вообще вам вызывать submitAll(), зависит от стратегии редактирования таблицы. По умолчанию установлена стратегия QSqlTableModel::OnRowChange, которая указывает, что произведенные изменения будут внесены в базу данных при выборе пользователем другой строки. Другие стратегии - это QSqlTableModel::OnManualSubmit (все изменения кэшируются в модели до вызова submitAll()) и QSqlTableModel::OnFieldChange (где изменения не кэшируются). Они, в основном, полезны, когда используется QSqlTableModel с представлением.
Хотя кажется, что QSqlTableModel::OnFieldChange обещает, что вам никогда не придется вызывать submitAll(). Есть два нюанса:
QSqlRelationalTableModel расширяет QSqlTableModel для предоставления поддержки внешних ключей. Внешний ключ - это связь один-к-одному, установленная между полем одной таблицы и полем первичного ключа другой таблицы. Например, если таблица book имеет поле с названием authorid, которое ссылается на поле id таблицы авторов, вы указываете, что поле authorid является первичным ключом.
Левый скриншот показывает QSqlTableModel в QTableView. Внешние ключи (city и country) не представлены удобочитаемыми значениями. Правый скриншот показывает QSqlRelationalTableModel, в котором внешние ключи представлены удобочитаемыми текстовыми строками.
Следующий отрывок кода демонстрирует настройку QSqlRelationalTableModel:
model->setTable("employee"); model->setRelation(2, QSqlRelation("city", "id", "name")); model->setRelation(3, QSqlRelation("country", "id", "name"));
Более подробную информацию можно найти в описании QSqlRelationalTableModel.
Классы QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel могут использоваться в качестве источников данных для классов представлений Qt, таких как QListView, QTableView и QTreeView. На практике наиболее часто используется QTableView в связи с тем, что результирующая SQL выборка, по существу, представляет собой двумерную структуру данных.
В следующем примере создается представление основанное на модели данных SQL:
QTableView *view = new QTableView; view->setModel(model); view->show();
Если модель является моделью для чтения-записи (например, QSqlTableModel), то представление позволяет редактировать поля. Вы можете отключить это с помощью следующего кода
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
Вы можете использовать одну и ту-же модель в качестве источника данных для нескольких представлений. Если пользователь изменяет данные модели с помощью одного из представлений, другие представления немедленно отобразят изменения. Пример Table Model демонстрирует, как это работает.
Классы-представления для обозначения колонок наверху отображают заголовки. Для изменения текста заголовка, вызовите setHeaderData() модели. Текстом заголовка по умолчанию является наименование поля. Например:
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name")); model->setHeaderData(2, Qt::Horizontal, QObject::tr("City")); model->setHeaderData(3, Qt::Horizontal, QObject::tr("Country"));
QTableView также имеет вертикальные заголовки слева, содержащие номера строк. Если вы программно вставляете строки с помощью QSqlTableModel::insertRows(), новые колонки будут обозначены звездочкой (*) до тех пор, пока они не будут помещены в базу данных с помощью submitAll() или автоматически при переходе пользователя к другой записи (если стратегия редактирования равна QSqlTableModel::OnRowChange).
Аналогично, если вы удаляете строки с помощью removeRows(), они будут отмечены восклицательным знаком (!), пока изменения не будут внесены в базу данных.
Элементы представления отображаются с помощью делегата. Делегат по умолчанию QItemDelegate содержит большинство общих типов данных (int, QString, QImage и т.д.). Также делегат отвечает за предоставление виджета-редактора (например, combobox), когда пользователь начинает редактировать элемент представления. Вы можете создавать свои собственные делегаты, наследуя QAbstractItemDelegate или QItemDelegate. Для получения более подробной информации смотрите Программирование с помощью модели/представления.
QSqlTableModel оптимизирован для работы с одной таблицей в один момент времени. Если Вам нужна модель для чтения-записи, которая может работать с произвольной результирующей выборкой, вы можете создать подкласс QSqlQueryModel и переопределить flags() и setData() для возможности осуществлять чтение-запись. Две следующие функции делают поля 1 и 2 модели SQL запроса редактируемыми:
Qt::ItemFlags EditableSqlModel::flags(
const QModelIndex &index) const
{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if (index.column() == 1 || index.column() == 2)
flags |= Qt::ItemIsEditable;
return flags;
}
bool EditableSqlModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
if (index.column() < 1 || index.column() > 2)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
if (index.column() == 1) {
ok = setFirstName(id, value.toString());
} else {
ok = setLastName(id, value.toString());
}
refresh();
return ok;
}
Вспомогательная функция setFirstName() определяется следующим образом:
bool EditableSqlModel::setFirstName(int personId, const QString &firstName) { QSqlQuery query; query.prepare("update person set firstname = ? where id = ?"); query.addBindValue(firstName); query.addBindValue(personId); return query.exec(); }
Функция setLastName() является подобной. Полный код реализации можно посмотреть в примере Query Model.
Создание подклассов модели делает возможной настройку собственной модели различными способами: вы можете предоставить подсказки к элементам, изменить цвет фона, отобразить вычисляемые значения, разрешать различным значениям быть отображенными и отредактированными, отдельно обработать нулевые значения и т.д. Для получения более подробной информации смотрите Программирование модель/представление и описание класса QAbstractItemView.
Если всё, что вам требуется - это разрешить внешний ключ для более понятного представления, вы можете использовать QSqlRelationalTableModel. Для получения лучшего результата, вам следует использовать делегат QSqlRelationalDelegate, который предоставляет для изменения значения внешнего ключа виджет-редактор в виде выпадающего списка.
Пример Relational Table Model иллюстрирует использование QSqlRelationalTableModel в совокупности с QSqlRelationalDelegate для представления таблицы с поддержкой внешнего ключа.
Использование SQL моделей описано выше, содержимое базы данных может быть представлено в других компонентах модель-представление. Для некоторых приложений достаточно, чтобы эти данные были представлены с помощью стандартных элементно-ориентированных представлений, таких как QTableView. Однако, пользователям приложений, основанных на манипулировании записями, часто требуется пользовательский интерфейс, содержащий формы, в котором данные из определенной строки или колонки в таблице базы данных используются, чтобы заполнять виджет-редактор на форме.
Такие информационно-связанные формы могут быть созданы с помощью класса QDataWidgetMapper, общего компонента модели-представления, который используется, чтобы отобразить данные из модели в определенный виджет в пользовательском интерфейсе.
QDataWidgetMapper работает с определенной таблицей базы данных, отображая элементы таблицы в базисе строка за строкой (row-by-row) или колонка за колонкой (column-by-column). В результате использование QDataWidgetMapper с SQL моделью так же просто, как использование его с любыми другими табличными моделями.
Демонстрация Books показывает, как информация может быть представлена для легкого доступа, используя QDataWidgetMapper и набор простых виджетов ввода.
[Предыдущая: Модуль QtScript] [Модули Qt] [Следующая: Модуль QtSvg]
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) | Торговые марки | Qt 4.5.3 |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |