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:
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:
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:
#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.