Qt - tworzenie połączenia z bazą danych i wysyłanie zapytań SQL do serwera

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 5703 razy

Wstęp

Do tworzenia połączenia z bazą danych SQL w Qt służy klasa QSqlDatabase. Niestety połączenie z bazą danych uruchomioną na lokalnym serwerze lub gdzieś na serwerze, do którego dostęp jest umożliwiony poprzez sieć lokalną czy też globalną wymaga odpowiedniego sterownika. W przypadku MySQL sterownik ten nie jest domyślnie dostępny. Oznacza to, że aby możliwe było realizowanie połączenia z bazą danych MySQL trzeba ściągnąć z strony dev.mysql.com/downloads/connector/c/ odpowiednią paczkę ze sterownikami zwracając uwagę na wersję Qt Creator-a zainstalowaną na komputerze, gdyż dla wersji 32-bitowej jest inna paczka a dla 64-bitowej inna. W moim przypadku pobrałem wersję 32-bitową i ją rozpakowałem a następnie plik libmysql.dll znajdujący się w folderze lib rozpakowanych plików należy przekopiować do folderu kompilatora, który w moim przypadku znajduje się w:

sciezka_do_folderu_z_Qt/Qt/Qt5.6.3/5.6.3/mingw49_32/bin

To jednak nie wszystko, albowiem zaprawdę powiadam wam, że konieczne jest jeszcze w pliku .pro dodanie ścieżki do plików nagłówkowych z rozpakowanej paczki:

INCLUDEPATH += sciezka_do_folderu_z_paczką/mysql-connector-c-6.1.11-win32/include

Skoro już jestem w pliku .pro to dodaję jeszcze następującą linijkę:

QT += sql

dzięki której będzie możliwy dostęp do plików nagłówkowych związanych z obsługą baz danych SQL.

Tworzenie w MySQL przykładową bazę danych do testów

Do testów konieczne jest utworzenie bazy danych oraz jakiegoś testowego użytkownika, który ma dostęp i pełne uprawnienia jedynie do tej bazy danych. Robię to w konsoli systemowej logując się w następujący sposób:

mysql -u root -p

Parametr -u oznacza, że wprowadzam nazwę użytkownika, zaś -p, że chcę podać hasło. Po zalogowaniu tworzę bazę danych i użytkownika oraz jakąś prostą tabelkę z jednym rekordem za pomocą następującego kody SQL:

CREATE DATABASE library CHARACTER SET utf8 COLLATE utf8_general_ci CREATE USER gienek@localhost IDENTIFIED BY 'password'; GRANT ALL ON library.* TO gienek@localhost; USE library; CREATE TABLE books ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title CHAR(255) NOT NULL, author CHAR(255) NOT NULL ); INSERT INTO books (title, author) VALUES ('Rio Anaconda', 'Wojciech Cejrowski');

W pierwszej linijce powyższego kodu tworzę bazę danych o nazwie library, w której kodowanie znaków zostało ustawione na UTF-8. W trzeciej linijce kodu tworzę użytkownika o dźwięcznej i niewiele znaczącej nazwie gienek, który ma dostęp do bazy danych jedynie przez localhost. Gienek to sprytny gość, więc do logowania używa niewymagającego zapamiętywania hasła o nazwie password. W piątej linijce gienkowi przydzielany jest przywilej korzystania z bazy danych library na wszystkie w niej znajdujące się tabele z możliwością wykonywania wszystkich dostępnych operacji. W siódmej linijce wchodzę do bazy danych library by po chwili z najdzikszą rozkoszą sięgającą granic pojmowania ludzkiego umysłu tworzę tabelę o przebiegłej nazwie books. Tabela ta zawiera pole id będące kluczem typu unsigned int z ustawioną opcją automatycznego przyrostu wartości przy dodawaniu rekordów do bazy danych. Kolejne pola tej tabeli to title i author, których maksymalna długość może osiągnąć zawrotną wartość 255 znaków. Po utworzeniu w tak żmudnym procesie tabeli niezwłocznie należy oddać się dzikiej rozkoszy dodania do niej jednej pozycji książkowej, co też i czynię w linijce piętnastej.

Baza danych - pierwszy kontakt

Oczywiście do utworzenia bazy danych jak i połączenia się z nią konieczna jest obecność w systemie uruchomionego serwera SQL w moim przypadku używam XAMPP-a, który zainstalował mi cały pakiet tego, co potrzebne jest do uruchomienia serwera SQL na localhost. W moim przypadku połączenie uzyskam wykorzystując następujący kod w pliku mainwindow.h:

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QDebug> #include <QMessageBox> #include <QStringList> #include <QSqlDatabase> #include <QSqlError> #include <QSqlQuery> #include <mysql.h> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT QSqlDatabase db; void readRecordsToListWidget(); public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_btAdd_clicked(); void on_records_doubleClicked(const QModelIndex &index); void on_btRemove_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H

W pliku mainwindow.cpp:

#include "mainwindow.h" #include "ui_mainwindow.h" // https://dev.mysql.com/downloads/connector/c/ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); db = QSqlDatabase::addDatabase("QMYSQL"); // tworzę bazę danych z wykorzystaniem sterownika QMYSQL db.setDatabaseName("library"); // ustawiam nazwę biblioteki, pod którą chcę się podpiąć db.setHostName("localhost"); // nazwa serwera db.setPassword("password"); // hasło gienka db.setPort(3306); // port połączenia z bazą danych db.setUserName("gienek"); // nazwa użytkownika if(!db.open()){ // próbuję otworzyć bazę danych i jak się nie otworzy to qDebug() << "ERROR load: " << db.lastError().text(); // wyświetlam błąd ładowania w konsoli kompilatora }else{ qDebug() << "Is opened"; // wszystko cacy readRecordsToListWidget(); // więc wczytuję dane do mojej kontrolki QListWidget } } void MainWindow::readRecordsToListWidget(){ QSqlQuery query(db); // tworzę obiekt zapytania i podpinam go pod otwartą bazę danych ui->records->clear(); // usuwam wszystkie elementy kontrolki if(query.exec("SELECT id, title, author FROM books")){ // wysyłam zapytanie do bazy danych while(query.next()){ // jeżeli są jakieś wyniki to // wstawiam rekord do kontrolki QListWidget ui->records->addItem(new QListWidgetItem(query.value(0).toString() + " \apos" + query.value(1).toString() + "\apos " + query.value(2).toString(), ui->records)); } }else{ qDebug() << query.lastError().text(); // wyświetlam w konsoli Qt Creatora informację błędu, gdy zapytanie zakończy się niepowodzeniem } } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_btAdd_clicked() // dodawanie elementu do bazy danych { // sprawdzanie, czy kontrolki edAuthor i edTitle nie zawierają niedozwolonych znaków if(ui->edAuthor->text().indexOf("\apos") != -1){ ui->edAuthor->setText( ui->edAuthor->text().replace("\apos", "") ); QMessageBox::information(this, "Ostrzeżenie", "Pole autora książki zawierało niedozwolony znak \apos, który został usunięty"); } if(ui->edTitle->text().indexOf("\apos") != -1){ ui->edTitle->setText( ui->edTitle->text().replace("\apos", "") ); QMessageBox::information(this, "Ostrzeżenie", "Pole tytułu książki zawierało niedozwolony znak \apos, który został usunięty"); } if(ui->edAuthor->text().size() && ui->edTitle->text().size()){ // jeżeli pola tekstowe nie są puste to QSqlQuery query(db); // tworzę obiekt zapytania // przygotowuję zapytanie SQL, sprawdzające, czy w bazie danych jest już książka o podanym tytule i autorze query.prepare("SELECT title, author FROM books WHERE title = :title AND author = :author LIMIT 1;"); // podpinam pod nie dane query.bindValue(":title", QVariant::fromValue(ui->edTitle->text())); // tekst z kontrolki edTitle zostanie oddzielnie wysłany a następnie wstawiony do zapytania za :title query.bindValue(":author", QVariant::fromValue(ui->edAuthor->text())); // to samo co powyżej if( query.exec() && (!query.next() // jak zapytanie zakończyło się powodzeniem i zwróciło element lub użytkownik postanowił dodać zduplikowany rekord to || QMessageBox::warning( // jeżeli this, "Ostrzeżenie", "Autor i tytuł już istnieje na liście, czy chcesz" " mimo to dodać tę pozycję ponownie?", QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes)){ // przygotowuję nowe zapytanie query.prepare("INSERT INTO books (title, author) VALUES (:title, :author);"); // binduję wartości query.bindValue(":title", QVariant::fromValue(ui->edTitle->text())); query.bindValue(":author", QVariant::fromValue(ui->edAuthor->text())); // wykonuję zapytanie if(query.exec()){ readRecordsToListWidget(); // i jeżeli zakończy się ono powodzeniem to odświerzam listę rekordów } } } } void MainWindow::on_records_doubleClicked(const QModelIndex &index) // zdarzenie kliknięcia dwukrotnego w oknie kontrolki { QString recordData = index.data().toString(); QStringList record = recordData.split(" \apos"); QStringList record2 = record[1].split("\apos "); record.removeAt(1); record += record2; if(record.size() == 3){ ui->edTitle->setText(record[1]); // wczytuję rekord do kontrolki tytułu ui->edAuthor->setText(record[2]); // wczytuję rekord do kontrolki autora } } void MainWindow::on_btRemove_clicked() // zdarzenie kliknięcia przycisku usunięcia zaznaczonego elementu { if(ui->records->currentItem()){ QString recordData = ui->records->currentItem()->data(Qt::DisplayRole).toString(); QStringList record = recordData.split(" \apos"); bool ok = false; int index = record[0].toInt(&ok); if(ok){ QSqlQuery query(db); query.prepare("DELETE FROM books WHERE id = :id;"); // zapytanie usuwające wybrany rekord query.bindValue(":id", index); if(query.exec()){ readRecordsToListWidget(); }else{ qDebug() << query.lastError(); } } } }

Poniżej można zobaczyć screen programu umożliwiającego dodawanie i usuwanie rekordów z bazy danych.

Przykład programu łączącego się z bazą danych MySQL
Rys. 1
Przykład programu łączącego się z bazą danych MySQL na localhoście
Strony powiązane
strony powiązane
  1. doc.qt.io/qt-5/qsqldatabase.html - opis kasy QSqlDatabase na stronie dokumentacji Qt
  2. doc.qt.io/qt-4.8/qsqlquery.html - opis kasy QSqlQuery na stronie dokumentacji Qt