c++ stl & qt. Занятие 05

37
Темы лекции: Базы данных в Qt. Практическое задание: Базы данных в Qt. Тренер: Игорь Шкулипа, к.т.н. С++ Библиотеки STL и Qt. Занятие 5

Upload: igor-shkulipa

Post on 21-Mar-2017

113 views

Category:

Education


1 download

TRANSCRIPT

Page 1: C++ STL & Qt. Занятие 05

Темы лекции: Базы данных в Qt.

Практическое задание: Базы данных в Qt.

Тренер: Игорь Шкулипа, к.т.н.

С++ Библиотеки STL и Qt. Занятие 5

Page 2: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 2

Адаптер

Паттерн Adapter, представляет собой программную обертку надсуществующими классами, преобразуя их интерфейсы к виду,пригодному для последующего использования.

Пусть класс, интерфейс которого нужно адаптировать к нужномувиду, имеет имя Adaptee. Для решения задачи преобразованияего интерфейса паттерн Adapter вводит следующую иерархиюклассов:

◦ Виртуальный базовый класс Target. Здесь объявляетсяпользовательский интерфейс подходящего вида. Только этотинтерфейс доступен для пользователя.

◦ Производный класс Adapter, реализующий интерфейс Target.В этом классе также имеется указатель или ссылка наэкземпляр Adaptee. Паттерн Adapter использует этотуказатель для перенаправления клиентских вызовов вAdaptee. Так как интерфейсы Adaptee и Target несовместимымежду собой, то эти вызовы обычно требуютпреобразования.

Page 3: C++ STL & Qt. Занятие 05

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;}

};

Page 4: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 4

Класс структуры

class FullName {

private:

string _strName; string _strSurname; string _strMiddle;

public:

FullName(string strSurname, string strName, string strMiddleName) {

_strName=strName; _strSurname=strSurname; _strMiddle=strMiddleName;

}

void Print() {

cout<<"Name: "<<_strName<<"\n";

cout<<"Middle Name: "<<_strMiddle<<"\n";

cout<<"Surname: "<<_strSurname<<"\n";

}

string GetName() {return _strName;}

string GetSurname() {return _strSurname;}

string GetMiddle() {return _strMiddle;}

void SetName(string strText) {_strName=strText;}

void SetSurname(string strText) {_strSurname=strText;}

void SetMiddle(string strText) {_strMiddle=strText;}

};

Page 5: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 5

Класс-адаптер

class FullNameAdapter: public InputStringFullName

{

public:

FullNameAdapter(InputStringFullName* stringFullName)

{

_strText=stringFullName->GetText();

}

FullName* GetFullName()

{

unsigned iFirstSpace=_strText.find_first_of(" ");

string strSurname=_strText.substr(0,iFirstSpace);

unsigned iSecondSpace=

_strText.substr(iFirstSpace+1).find_first_of(" ")+iFirstSpace+1;

string strName=_strText.substr(iFirstSpace, iSecondSpace-iFirstSpace);

string strMiddle=_strText.substr(iSecondSpace);

return new FullName(strSurname, strName, strMiddle);

}

};

Page 6: C++ STL & Qt. Занятие 05

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

Page 7: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 7

Команда

Паттерн Command преобразовывает запрос на выполнениедействия в отдельный объект-команду. Такая инкапсуляцияпозволяет передавать эти действия другим функциям иобъектам в качестве параметра, приказывая им выполнитьзапрошенную операцию. Команда – это объект, поэтому надней допустимы любые операции, что и над объектом.

Команда используется, если:

◦ Система управляется событиями. При появлении такогособытия (запроса) необходимо выполнить определеннуюпоследовательность действий.

◦ Необходимо параметризировать объекты выполняемымдействием, ставить запросы в очередь или поддерживатьоперации отмены и повтора действий.

◦ Нужен объектно-ориентированный аналог функцииобратного вызова в процедурном программировании.

Page 8: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 8

Пример. Интерфейс «Игра»

class IGame

{

public:

virtual void New()=0;

virtual void Start()=0;

virtual void Pause()=0;

virtual void Resume()=0;

virtual void Finish()=0;

virtual void Break()=0;

virtual void BreakAndFinish()=0;

virtual void BreakAndStart()=0;

};

Page 9: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 9

Реализация интерфейса

class SomeGame: public IGame {

public:

virtual void New() {

cout<< "New Game.\n“; }

virtual void Start() {

cout<< "Game Started.\n“; }

virtual void Pause() {

cout<< "Game Paused.\n“; }

virtual void Resume() {

cout<< "Game Resumed.\n“; }

virtual void Finish() {

cout<< "Game Finished.\n“; }

virtual void Break() {

cout<< "Game Breaked.\n“; }

virtual void BreakAndFinish() {

this->Break(); this->Finish(); }

virtual void BreakAndStart() {

this->Break(); this->New(); this->Start(); }

};

Page 10: C++ STL & Qt. Занятие 05

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

} };

Page 11: C++ STL & Qt. Занятие 05

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

} };

Page 12: C++ STL & Qt. Занятие 05

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

} };

Page 13: C++ STL & Qt. Занятие 05

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.

Page 14: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 14

Подключение к базе

Класс QSqlDatabase предоставляет интерфейс для подключения к базеданных через соединение. Экземпляр класса QSqlDatabaseпредставляет соединение. Соединение предоставляет доступ к базеданных через один из поддерживаемых драйверов баз данных,которые унаследованы от QSqlDriver. В качестве альтернативы,можно создать класс-наследник от QSqlDriver для драйвера своей базыданных.

Создать соединение можно с помощью одной из статических функцийaddDatabase(), которой передается используемый драйвер или типдрайвера и имя соединения.

Соединение известно под своим собственным именем, а не по имени базыданных с которой оно соединяет. Можно иметь множество соединенийс одной и той же базой данных.

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

Page 15: C++ STL & Qt. Занятие 05

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

Page 16: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 16

Пример соединения

QSqlDatabase db = QSqlDatabase::addDatabase(“QMYSQL”);

db.setHostName(“somehost.com”);

db.setDatabaseName(“mydb”);

db.setUserName(“admin”);

db.setPassword(“pjgf”);

bool ok = db.open();

Page 17: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 17

Выполнение запросовКласс QSqlQuery предоставляет возможность выполнения любой

инструкции SQL, поддерживаемой используемой базой данных.

Перемещения по записям осуществляется следующими методами:

• next()

• previous()

• first()

• last()

• seek()

Эти функции позволяют двигаться вперед, назад или произвольно позаписям, возвращаемым запросом.

Если вам нужно двигаться только вперед по результатам, можноиспользовать setForwardOnly(), что позволит сэкономитьзначительный объем памяти, снизить накладные расходы и повыситьпроизводительность на некоторых базах данных.

Если активный запрос находится на действительной записи, данныеможно получить с помощью value(). Все данные передаются извнутреннего интерфейса SQL с использованием QVariant.

Page 18: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 18

Пример запроса

QSqlQuery query;

query.exec("SELECT u_name FROM users");

while (query.next()) {

QString name = query.value(0).toString();

doSomething(name);

}

Page 19: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 19

Проверка ошибок запроса

Можно проверить наличие ошибок запроса, вызвав метод IsActive()

if (!query.isActive())

{

QMessageBox *mb=new QMessageBox();

mb->setText(query.lastError().text());

mb->show();

}

Page 20: C++ STL & Qt. Занятие 05

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 в случае ошибки.

Page 21: C++ STL & Qt. Занятие 05

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

Page 22: C++ STL & Qt. Занятие 05

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

• Добавлена кнопка регистрации на основное окно.

Page 23: C++ STL & Qt. Занятие 05

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;

};

Page 24: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 24

Реализация подключения

#include "dbconnection.h"

#include <QtDebug>

#include <QSqlError>

DBConnection* DBConnection::_connection=NULL;

DBConnection::DBConnection(){

_database=QSqlDatabase::addDatabase("QODBC3");

_database.setHostName("localhost");

_database.setDatabaseName(

"Driver={Microsoft Access Driver (*.mdb, *.accdb)};

DSN='';DBQ=D:\\a\\qtdb.accdb");

_database.setUserName("");

_database.setPassword("");

if (!_database.open())

{qDebug() << _database.lastError().text();}

}

QSqlQuery DBConnection::execQuery(QString queryText){

return QSqlQuery(queryText);

}

DBConnection::~DBConnection(){

_database.close();

}

Page 25: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 25

Новый конструктор модели

Model::Model()

{

DBConnection* db= DBConnection::GetInstance();

QSqlQuery q= db->execQuery

("SELECT u_id, u_name, u_password FROM users;");

if (!q.isActive())

{

qDebug() << q.lastError().text());

}

if (q.isActive())

{

while(q.next())

{

QString uname=q.value(1).toString();

QString upass=q.value(2).toString();

usersAndPasswords.insert(uname, upass);

}

}

}

Page 26: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 26

Методы вставки

void Model::insertQueryPositionStyle(QString user, QString password) {

QSqlQuery q;

q.prepare("INSERT INTO users (u_name, u_password) VALUES (?,?)");

q.addBindValue(user);

q.addBindValue(password);

q.exec();

}

void Model::insertQueryTextStyle(QString user, QString password) {

QSqlQuery q

("INSERT INTO users (u_name, u_password) VALUES

(\'"+user+"\',\'"+password+"\');");

if (!q.isActive())

{

qDebug() << q.lastError().text());

}

}

void Model::InsertUser(QString user, QString password) {

insertQueryPositionStyle(user, password);

}

Page 27: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 27

Результат

Page 28: C++ STL & Qt. Занятие 05

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

Page 29: C++ STL & Qt. Занятие 05

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

Page 30: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 30

Шаблон интерфейса «master-detail»

Классическим примером для любого средства разработки интерфейса кБД является отражение связи "родитель-потомок" (Mater-Detail).

Page 31: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 31

Пример. Основное окно

Page 32: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 32

Пример. База данных

Page 33: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 33

Заголовок окна#include <QMainWindow>

#include <QModelIndex>

#include <QSqlTableModel>

#include <QSqlRelationalTableModel>

#include <QSqlDatabase>

namespace Ui {

class MainWindow;

}

class MainWindow : public QMainWindow {

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0);

~MainWindow();

private slots:

void on_tableView_clicked(const QModelIndex &index);

void on_pushButton_clicked();

private:

Ui::MainWindow *ui;

QSqlRelationalTableModel* masterModel;

QSqlTableModel* detailModel;

};

Page 34: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 34

Реализация окна#include "mainwindow.h"

#include "ui_mainwindow.h"

#include <QDebug>

#include <QSqlError>

#include <QSqlRecord>

#include <QAbstractItemView>

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent), ui(new Ui::MainWindow) {

ui->setupUi(this);

ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);

QSqlDatabase database=QSqlDatabase::addDatabase("QODBC3");

database.setHostName("localhost");

database.setDatabaseName

("Driver={Microsoft Access Driver (*.mdb, *.accdb)};

DSN='';DBQ=D:\\a\\qtdb.accdb");

database.setUserName(""); database.setPassword("");

if (!database.open()) {qDebug() << database.lastError().text();}

masterModel=new QSqlRelationalTableModel(this, database);

detailModel=new QSqlTableModel(this, database);

}

MainWindow::~MainWindow() {

delete ui;

delete masterModel;

delete detailModel;

}

Page 35: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 35

Реализация окна

void MainWindow::on_tableView_clicked(const QModelIndex &index) {

detailModel->setTable("people");

detailModel->setFilter(QString("p_uid=%1").arg(index.row()+1));

detailModel->select();

QString text="";

if (detailModel->rowCount()>0) {

text+=detailModel->record(0).value(0).toString()+"\n";

text+=detailModel->record(0).value(1).toString()+"\n";

text+=detailModel->record(0).value(2).toString()+"\n";

text+=detailModel->record(0).value(3).toString()+"\n";

text+=detailModel->record(0).value(4).toString();

}

ui->textEdit->setText(text);

}

void MainWindow::on_pushButton_clicked() {

masterModel->setTable("users");

masterModel->setRelation(0, QSqlRelation("people", "p_id", "p_name"));

masterModel->setHeaderData(0, Qt::Horizontal, "Name");

masterModel->setHeaderData(1, Qt::Horizontal, "User");

masterModel->setHeaderData(2, Qt::Horizontal, "Password");

masterModel->select();

ui->tableView->setModel(masterModel);

}

Page 36: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 36

Результат

Page 37: C++ STL & Qt. Занятие 05

http://www.slideshare.net/IgorShkulipa 37

Лабораторная работа №5. Базы данных

Добавить к тетрису возможность регистрации пользователей и веденияполученных очков.

Реализовать отображение пользователей, отсортированных по рейтингу,на основном окне приложения с применением шаблона «master-detail»