Home · All Classes · Main Classes · Grouped Classes · Modules · Functions

[Предыдущая: Модуль QtOpenGL] [Модуль Qt] [Следующая: Модуль QtSvg]

Модуль QtSql

Модуль QtSql помогает реализовать бесшовную реализацию Ваших приложений с базами данных. Далее...

Пространства Имен

QSqlРазличные идентификаторы, используемые в разных библиотеках Qt SQL

Классы

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Модуль данных для редактирования одной таблицы базы данных

Подробное Описание

Модуль QtSql помогает реализовать бесшовную реализацию Ваших приложений с базами данных.

Классы SQL подразделяются на три уровня:

УровеньОписание
Уровень ДрайвераВключает классы QSqlDriver, QSqlDriverCreator<T>, QSqlDriverCreatorBase, QSqlDriverPlugin и QSqlResult. Этот слой предоставляет низкоуровневый мост между специфичными базами данных и уровнем SQL API. Для получения более подробной информации, см. Драйвера Баз Данных SQL.
Уровень SQL APIЭти классы предоставляют доступ к базам данных. Связь устанавливается с помощью класса QSqlDatabase. Взаимодействие с базой данных осуществляется с помощью класса QSqlQuery. В дополнение к классам QSqlDatabase и QSqlQuery, слой SQL API поддерживается классами QSqlError, QSqlField, QSqlIndex и QSqlRecord.
Уровень Пользовательского ИнтерфейсаЭти классы связывают данные базы данных с виджетами, знающими, как с этими данными обращаться. Сюда входят такие классы, как QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel. Эти классы разработаны для работы с каркасом модель/представление Qt.

Для включения определений классов модуля, используйте следующую директиву:

    #include <QtSql>

Для сборки Вашего проекта с модулем, добавьте в Ваш qmake .pro-файл следующую строку:

    QT += sql

Модуль QtSql входит в состав Qt Console Edition, Qt Desktop Edition и Qt Open Source Edition.

Данный обзор предполагает, что Вы имеете, по крайней мере, базовые знания об 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 также поддерживает концепцию соединения по умолчанию, которое используется классом Qt SQL если никакое другое соединение не указано. Этот механизм очень удобен для приложений, использующих только одно соединение с базой данных.

Следующий код устанавливает соединение с базой данных MySQL с именем flightdb на хосте bigblue:

        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
        db.setHostName("bigblue");
        db.setDatabaseName("flightdb");
        db.setUserName("acarlson");
        db.setPassword("1uTbSbAs");
        bool ok = db.open();

Первый аргумент QSqlDatabase::addDatabase() - это имя драйвера. Для получения списка драйверов, см. документацию addDatabase(). Для инициализации данных соединения мы вызываем setHostName(), setDatabaseName(), setUserName() и setPassword().

Так как имя соединения не определено, соединение становится соединением по умолчанию. Для определения имени, передайте его в качестве второго аргумента в QSqlDatabase::addDatabase(). Например:

        QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first");
        QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second");

Как только соединение инициализировано, мы должны вызвать QSqlDatabase::open() для открытия бызы данных и получения доступа к данным. Если запрос соединения потерпит неудачу, функция возвратит false; информацию об ошибке может быть получена из QSqlDatabase::lastError().

Как только соединение установлено, мы можем получить указатель на него из любого места прораммы, вызвав статическую функцию QSqlDatabase::database(). Если мы вызываем данную функцию без параметра, она возвратит указатель на соединение по умолчанию. Если функция вызывается с идентификатором, использованным при установке соединения, она возвратит ссылку не указанное соединение. Например:

        QSqlDatabase defaultDB = QSqlDatabase::database();
        QSqlDatabase firstDB = QSqlDatabase::database("first");
        QSqlDatabase secondDB = QSqlDatabase::database("second");

Для удаления соединения с базой данных, сперва закройте базу данных с помощью QSqlDatabase::close(), а затем, удалите ее с помощью статического метода QSqlDatabase::removeDatabase().

Выполнение Инструкций SQL

Класс 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.

Вы можете перемещаться взад и вперед по выборке, используя функции 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 {
            // this can be very slow
            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 просто отправляет запрос в DBMS; в противном случае, Qt само разбирает синтаксис и формирует запрос. Фактический запрос, который поступает на выполнение в DBMS доступен с помощью 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();

Транзакции можно использовать для того, чтобы гарантировать атомарность сложного действия (например, просмотр внешней ключей и создание записи) или для возможности отмены сложного действия в процессе его выполнения.

Использование Классов-Моделей SQL

В дополнение к QSqlQuery, Qt предлагает три высокоуровневых класса для работы с базами данных. Это классы QSqlQueryModel, QSqlTableModel и QSqlRelationalTableModel.

QSqlQueryModelМодель только-для-чтения, основаннная на произвольных SQL-запросах.
QSqlTableModelМодель для чтения-записи работающая с одной таблицей.
QSqlRelationalTableModelПодкласс QSqlTableModel с поддержкой внешних ключей.

Эти классы происходят от QAbstractTableModel (который в свою очередь происходит от QAbstractItemModel) и могут существенно облегчить представление данных базы данных в классах-представлениях элементов, таких как QListView и QTableView. Это подробно объясняется в разделе Отображение Данных в Представлении-Таблице.

Другое преимущество использования этих классов состоит в том, что они облегчают приспособление кода к другому источнику данных. Например, если Вы использовали QSqlTableModel, а затем для хранения данных решили использовать вместо базы XML-файлы, то изменение кода - это вопрос замены одной модели данных другой.

Использование Модели Запроса SQL

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 для установки запроса.

Модель Таблицы SQL

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();

Для доступа к данным Вы можете использовать унаследованные от QAbstractItemModel QSqlTableModel::data() и QSqlTableModel::setData(). В следующем примере показано, как изменить запись с помощью 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(). Есть два нюанса:

Модель Реляционной Таблицы SQL

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, по существу, представляет собой двумерную структуру данных.

A table view displaying a QSqlTableModel

В следующем примере создается представление основанное на модели данных 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).

Inserting a row in a model

Аналогично, если Вы удаляете строки с помощью removeRows(), строки будут отмечены восклицательным знаком (!) пока изменения не будут внесены в базу данных.

Элемены представления отображаются с помощью делегата. Делегат по умолчанию, QItemDelegate, большинство общих типов данных (int, QString, QImage и т.д.). Также делегат отвечает за предоставление виджета-редактора (например, combobox) при начале редактирования пользователем элемента представления. Вы можете создавать свои собственные делегаты наследуя QAbstractItemDelegate или QItemDelegate. Для получения более подробной информации см. Программирование Модель/Представление.

QSqlTableModel оптимизирован для работы с одной таблицей в один момент времени. Если Вам нужна модель для чтения-записи, которая может работать с произвольной результирующей выборкой, Вы можете создать подкласс QSqlQueryModel и заново реализовать flags() и setData() для возможности осуществлять чтение-запись. Две следующие функции делают поля 1 и 2 модели запроса редактируемыми:

    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, который предоставляет для изменения значения внешней ссылки виджет редактор в виде выпадающего списка.

Editing a foreign key in a relational table

Пример Relational Table Model иллюстрирует использование QSqlRelationalTableModel в совокупности с QSqlRelationalDelegate для представления таблицы с поддержкой внешней связи.

[Предыдущая: Модуль QtOpenGL] [Модули Qt] [Следующая: Модуль QtSvg]


Copyright © 2005 Trolltech Trademarks
Qt 4.1.0
Hosted by uCoz