1. Темы лекции: Базы данных в Qt.
Практическое задание: Базы данных в Qt.
Тренер: Игорь Шкулипа, к.т.н.
С++ Библиотеки STL и Qt. Занятие 5
2. http://www.slideshare.net/IgorShkulipa 2
Адаптер
Паттерн Adapter, представляет собой программную обертку над
существующими классами, преобразуя их интерфейсы к виду,
пригодному для последующего использования.
Пусть класс, интерфейс которого нужно адаптировать к нужному
виду, имеет имя Adaptee. Для решения задачи преобразования
его интерфейса паттерн Adapter вводит следующую иерархию
классов:
◦ Виртуальный базовый класс Target. Здесь объявляется
пользовательский интерфейс подходящего вида. Только этот
интерфейс доступен для пользователя.
◦ Производный класс Adapter, реализующий интерфейс Target.
В этом классе также имеется указатель или ссылка на
экземпляр Adaptee. Паттерн Adapter использует этот
указатель для перенаправления клиентских вызовов в
Adaptee. Так как интерфейсы Adaptee и Target несовместимы
между собой, то эти вызовы обычно требуют
преобразования.
3. http://www.slideshare.net/IgorShkulipa 3
Пример. Преобразование строки в структуру
class InputStringFullName {
protected:
string _strText;
public:
InputStringFullName() {
_strText="";
}
void Input() {
cout<<"Input Full Name (Surname Name MiddleName): ";
char* cstr=new char;
cin.getline(cstr, 9999, 'n');
_strText=string(cstr);
}
string GetText(){return _strText;}
};
6. http://www.slideshare.net/IgorShkulipa 6
Использование адаптера
int main()
{
InputStringFullName* isfn=new InputStringFullName();
isfn->Input();
FullNameAdapter* fna=new FullNameAdapter(isfn);
FullName* fn=fna->GetFullName();
fn->Print();
}
Результат:
Input Full Name (Surname Name MiddleName): Ivanov Petr Sidorovich
Name: Petr
Middle Name: Sidorovich
Surname: Ivanov
7. http://www.slideshare.net/IgorShkulipa 7
Команда
Паттерн Command преобразовывает запрос на выполнение
действия в отдельный объект-команду. Такая инкапсуляция
позволяет передавать эти действия другим функциям и
объектам в качестве параметра, приказывая им выполнить
запрошенную операцию. Команда – это объект, поэтому над
ней допустимы любые операции, что и над объектом.
Команда используется, если:
◦ Система управляется событиями. При появлении такого
события (запроса) необходимо выполнить определенную
последовательность действий.
◦ Необходимо параметризировать объекты выполняемым
действием, ставить запросы в очередь или поддерживать
операции отмены и повтора действий.
◦ Нужен объектно-ориентированный аналог функции
обратного вызова в процедурном программировании.
10. http://www.slideshare.net/IgorShkulipa 10
Классы команд
class ICommand {
public:
ICommand(IGame* game){ _game=game; }
virtual void ExecuteCommand()=0;
virtual ~ICommand(){ delete _game;}
protected: IGame* _game;
};
class NewCommand: public ICommand {
public:
NewCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand(){
cout<<"Executing New Command...n";
_game->New();
} };
class StartCommand: public ICommand {
public:
StartCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Start Command...n";
_game->Start();
} };
11. http://www.slideshare.net/IgorShkulipa 11
Классы команд
class PauseCommand: public ICommand {
public:
PauseCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Pause Command...n";
_game->Pause();
} };
class ResumeCommand: public ICommand {
public:
ResumeCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Resume Command...n";
_game->Resume();
} };
class FinishCommand: public ICommand {
public:
FinishCommand(IGame* game) :ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Finish Command...n";
_game->Finish();
} };
12. http://www.slideshare.net/IgorShkulipa 12
Классы команд
class BreakCommand: public ICommand {
public:
BreakCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Break Command...n";
_game->Break();
} };
class BreakAndFinishCommand: public ICommand {
public:
BreakAndFinishCommand(IGame* game):ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Break And Finish Command...n";
_game->BreakAndFinish();
} };
class BreakAndStartCommand: public ICommand {
public:
BreakAndStartCommand(IGame* game) :ICommand(game){}
virtual void ExecuteCommand() {
cout<<"Executing Break And Start Command...n";
_game->BreakAndStart();
} };
13. http://www.slideshare.net/IgorShkulipa 13
Использование команд
int main()
{
IGame* someGame=new SomeGame();
ICommand* command[10];
command[0]=new NewCommand(someGame);
command[1]=new StartCommand(someGame);
command[2]=new BreakCommand(someGame);
command[3]=new NewCommand(someGame);
command[4]=new StartCommand(someGame);
command[5]=new BreakAndStartCommand(someGame);
command[6]=new PauseCommand(someGame);
command[7]=new ResumeCommand(someGame);
command[8]=new PauseCommand(someGame);
command[9]=new BreakAndFinishCommand(someGame);
for (int i=0;i<10;i++)
{
command[i]->ExecuteCommand();
}
}
Результат:
Executing New Command...
New Game.
Executing Start Command...
Game Started.
Executing Break Command...
Game Breaked.
Executing New Command...
New Game.
Executing Start Command...
Game Started.
Executing Break And Start Command...
Game Breaked.
New Game.
Game Started.
Executing Pause Command...
Game Paused.
Executing Resume Command...
Game Resumed.
Executing Pause Command...
Game Paused.
Executing Break And Finish Command...
Game Breaked.
Game Finished.
14. http://www.slideshare.net/IgorShkulipa 14
Подключение к базе
Класс QSqlDatabase предоставляет интерфейс для подключения к базе
данных через соединение. Экземпляр класса QSqlDatabase
представляет соединение. Соединение предоставляет доступ к базе
данных через один из поддерживаемых драйверов баз данных,
которые унаследованы от QSqlDriver. В качестве альтернативы,
можно создать класс-наследник от QSqlDriver для драйвера своей базы
данных.
Создать соединение можно с помощью одной из статических функций
addDatabase(), которой передается используемый драйвер или тип
драйвера и имя соединения.
Соединение известно под своим собственным именем, а не по имени базы
данных с которой оно соединяет. Можно иметь множество соединений
с одной и той же базой данных.
QSqlDatabase также поддерживает концепцию соединения по умолчанию,
которое является неименованным (unnamed) соединением. Чтобы
создать соединения по умолчанию при вызове addDatabase() не
передавайте аргумент с именем соединения.
15. http://www.slideshare.net/IgorShkulipa 15
Драйвера баз данных Qt
Тип драйвера Описание
QDB2 IBM DB2
QIBASE Драйвер Borland InterBase
QMYSQL Драйвер MySQL
QOCI Драйвер Oracle Call Interface
QODBC Драйвер ODBC (включая Microsoft SQL Server)
QPSQL Драйвер PostgreSQL
QSQLITE SQLite версии 3 или выше
QSQLITE2 SQLite версии 2
QTDS Драйвер Sybase Adaptive Server
17. http://www.slideshare.net/IgorShkulipa 17
Выполнение запросов
Класс QSqlQuery предоставляет возможность выполнения любой
инструкции SQL, поддерживаемой используемой базой данных.
Перемещения по записям осуществляется следующими методами:
• next()
• previous()
• first()
• last()
• seek()
Эти функции позволяют двигаться вперед, назад или произвольно по
записям, возвращаемым запросом.
Если вам нужно двигаться только вперед по результатам, можно
использовать setForwardOnly(), что позволит сэкономить
значительный объем памяти, снизить накладные расходы и повысить
производительность на некоторых базах данных.
Если активный запрос находится на действительной записи, данные
можно получить с помощью value(). Все данные передаются из
внутреннего интерфейса SQL с использованием QVariant.
19. http://www.slideshare.net/IgorShkulipa 19
Проверка ошибок запроса
Можно проверить наличие ошибок запроса, вызвав метод IsActive()
if (!query.isActive())
{
QMessageBox *mb=new QMessageBox();
mb->setText(query.lastError().text());
mb->show();
}
20. http://www.slideshare.net/IgorShkulipa 20
Запрос вставки данных
Запросы INSERT, UPDATE, DELETE и DROP выполняются так же, как и
SELECT (передаются в текстовом виде).
QSqlQuery query;
query.exec(“INSERT INTO users VALUES(1, ‘User1’, ‘Password1’)”);
или
QSqlQuery query(“INSERT INTO users VALUES(1, ‘User1’, ‘Password1’)”);
После этого метод numRowsAffected() возвращает количество строк,
которые были изменены инструкцией запроса или -1 в случае ошибки.
21. http://www.slideshare.net/IgorShkulipa 21
Вставка по позициям
Если необходимо избежать преобразования значений в строковые
данные, то можно воспользоваться методом prepare() для создания
шаблона запроса с последующим присваиванием данных.
QSqlQuery q;
q.prepare(
"INSERT INTO users (u_id, u_name, u_password)
VALUES (?,?,?)");
q.addBindValue(1);
q.addBindValue(“User1”);
q.addBindValue(“Password1”);
q.exec();
22. http://www.slideshare.net/IgorShkulipa 22
Пример. См. Предыдущую презентацию.
Дополнение к проекту «MVP»:
• Добавлен сигнал к IView - virtual void onRegister()=0;
• Добавлен слот к презентеру - void Register();
• Добавлен метод в модель –
void InsertUser(QString user, QString password);
• Добавлено два приватный метода в модель –
void insertQueryTextStyle(QString user, QString password);
void insertQueryPositionStyle(QString user, QString password);
• Добавлена кнопка регистрации на основное окно.
23. http://www.slideshare.net/IgorShkulipa 23
Класс-одиночка «Подключение к базе»
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlDriver>
class DBConnection
{
public:
static DBConnection* GetInstance(){
if (_connection==NULL) _connection= new DBConnection();
return _connection;
}
~DBConnection();
QSqlQuery execQuery(QString queryText);
private:
DBConnection();
static DBConnection* _connection;
QSqlDatabase _database;
};
28. http://www.slideshare.net/IgorShkulipa 28
QSqlTableModel
Кроме QSqlQuery, Qt содержит класс QSqlTableModel – это интерфейс
высокого уровня, который позволяет не использовать SQL в чистом
виде для выполнения запросов.
QSqlTableModel model;
model.setTable(“users”);
model.setFilter(“id>10”);
model.select();
Просмотр результата запроса осуществляется с помощью метода record()
и доступа к отдельным полям с помощью метода value().
Метод value() принимает в качестве аргумента либо индекс поля, либо его
имя.
QSqtring user=model.record().value(1).toString();
QSqtring password=model.record().value(“u_password”).toString();
29. http://www.slideshare.net/IgorShkulipa 29
Вставка данных
Для вставки данных можно воспользоваться методом insertRows() для
создания новой пустой строки в таблице, а затем использовать
setData() для установки значения каждого столбца.
QSqlTableModel model;
model.setTable(“users”);
int row=0;
model.insertRows(row, 1);
model.setData(model.index(row,0), 1);
model.setData(model.index(row,0), “User1”);
model.setData(model.index(row,0), “Password1”);
model.submitAll();
37. http://www.slideshare.net/IgorShkulipa 37
Лабораторная работа №5. Базы данных
Добавить к тетрису возможность регистрации пользователей и ведения
полученных очков.
Реализовать отображение пользователей, отсортированных по рейтингу,
на основном окне приложения с применением шаблона «master-detail»