ВВЕДЕНИЕ
Администрирование баз данных в современных крупных сервисах имеет большое значение. Магазины, социальные сети, хостинги медиаконтента – все используют базы данных для хранения данных, учетных записей пользователей, данных о хранимом содержимом, информация о товаре и т.д.
Существует большой выбор баз данных, современных и удобных в использовании, но не все предлагают тот набор возможностей, который хотелось бы иметь.
Таким образом было принято решение создать клиент для пока что одной системы управления базами данных (СУБД) – MYSQL. В дальнейшем, возможно добавление как серверных СУБД, например, SQL Server, так и баз данных типа файл – клиент, например, Access и SQLite.
1. Выбор способа и среды реализации графического клиента
Приложение, которое будет взаимодействовать с базами данных, от малых до крупных, должно быть быстрым и запускаться на большинстве систем, поэтому было принято решение создать его в среде Qt на языке C++.
Среди многих современных языков, поддерживающих объектно-ориентированный подход при решении задач, вроде Java или C# есть одна распространенная проблема – медленная скорость работы ценой удобства программиста. Такие важные вещи, вроде управления памятью, за которые в С++ несет ответственность программист, в C# реализует сборщик мусора. В свою очередь, отказ от низкоуровневых элементов, вроде того, что был указан выше обуславливает главную проблему – показатели скорости заметно снижаются.
Создав приложение на С++ и оптимизировав его, можно будет смело запускать его на слабых компьютерах и не боятся низкой производительности.
2. Общее описание программного продукта
У самой MYSQL уже имеется в комплекте графическая оболочка для администрирования, которая предлагает достаточный набор инструментов и функций для работы с сервером баз данных, администрированием пользователей и написанием скриптов и простых запросов.
По большей части создание клиента это получение опыта проектирования и написания достаточно крупного приложения с реализацией дополнительного набора функций, который не идет вместе с MYSQL «из коробки». Например, реализовать удобный графический интерфейс для быстрого создания таблицы с заданными параметрами и последующее ее заполнение из текстового документа данными. Иногда хочется, чтобы часть сложной работы за тебя выполняла программа. Сложные вещи, вроде создания запросов на выбор, удаление, обновление с множеством параметров, вложенными запросами, хотелось бы заменить диалоговым окном с набором полей и параметров, которые создадут запрос за пользователя, выполнят его и отобразят результат, если таковой имеется.
Первым делом был разработан основной интерфейс программы. Изначально все поля названы на английском, но в дальнейшем будет создан файл перевода на русский язык и введена возможность динамической смены языка во время выполнения.
Рисунок 1 - Главное диалоговое окно программы
Идея очень простая – слева расположен виджет дерева, который будет реализовывать простую идею взаимодействия пользователя с базой данных, раскрывая сначала список баз данных, а затем список таблиц, которые содержат эти базы (рисунок 1).
Над виджетом находится поле для поиска нужной таблицы. При вводе текста дерево автоматически удаляет не подходящие под запрос таблицы и отображает те, что содержат, введенный пользователем текст.
Справа расположено поле со вкладками, которые пустуют, пока пользователь не выберет нужную ему таблицу. При двойном щелчке по таблице из списка, для нее сразу же создастся вкладка в правом разделе и отобразится ее содержимое.
Данный виджет поддерживает большое количество одновременно открытых вкладок, а при попытке открыть уже существующую в списке таблицу, он просто изменит активную вкладку на выбранную.
Рисунок 2 - Окно программы, с открытыми таблицами
Как видно из изображения на рисунке 2, вкладка не только содержит данные из самой таблицы, но и информацию за сколько времени был осуществлен запрос к базе данных. В примере используется локальная база данных, поэтому реальные результаты могут сильно отличаться в зависимости от загруженности сервера, а также от пропускной способности сети клиента.
Одна из основных целей этого клиента – позволить быстро и удобно взаимодействовать с таблицами, хранящимися на сервере баз данных, поэтому для оптимизации было решено загружать все таблицы изначально в режиме только для чтения, так как открытие их для редактирования сильно замедляет процесс. Так же в MYSQL имеются системные и вспомогательные базы данных, которые система редактировать запрещает.
Было решено добавить в контекстное меню несколько действий вроде вставки новой строки, удаления выделенных строк и переключения режимов просмотра. Открытая таблица во вкладке может отображаться в двух режимах – просмотра и редактирования. Сделано это в целях оптимизации и защиты данных от случайного изменения.
Рисунок 3 - Таблица в режиме редактирования
Переключая таблицу в режим редактирования, автоматически создается пустая строка для заполнения, если пользователь не заполнит ее, то она не добавится в базу.
Удаление строк производится обычным выделением и выбором действия удаления из контекстного меню. При чем не обязательно выделять строки целиком, достаточно выделить один столбец на нескольких строках.
Если программа на данном этапе не содержит нужных функций, то их можно заменить вручную, выполнив запрос. Запрос к таблице или базе можно выполнить при помощи действия во вкладке меню Запрос. В данный момент доступно лишь выполнение простых запросов, но в дальнейшем планируется реализовать возможность выполнения пользовательских скриптов с поддержкой транзакций.
Рисунок 4 - Окно ввода запроса
Рисунок 5 - Результат выполнения запроса.
Окно ввода запроса поддерживает выделение синтаксиса языка SQL. Поддерживается более 600 ключевых слов языка.
На изображении видно, что пользователь вместо написания полного названия таблицы, включая в него название базы данных, может просто указать необходимую базу данных из списка.
Программа сама понимает, когда пользователь выполняет тот или иной запрос и отображает дополнительную строку с выбором базы данных. Например, если нам необходимо выбрать какие-то данные запросом SELECT, то при вводе этого слова в поле ввода, в окне автоматически отобразится виджет выпадающего списка с базами данных. Если же пользователь будет лишь изменять данные запросами вроде DELETE или UPDATE, то окно с результатом запроса, отображено не будет.
Данный клиент так же позволяет экспортировать таблицы в различные форматы. В текущей версии поддерживается экспорт в html, csv и pdf. В pdf экспорт пока что не доделан до конца, в будущем планируется реализовать систему, похожую на отчеты в среде Microsoft Office Access и их экспорт в pdf. В текущей же версии происходит простое сохранение таблицы, что не особо полезно.
Иначе дело обстоит с экспортом в csv. Иногда появляется необходимость получить данные из базы и сохранить их в формат, который затем можно будет отредактировать в excel или других табличных редакторах или импортировать в другие приложения. В таких ситуация можно применить простой и универсальный формат – csv. Он позволяет хранить табличные данные в текстовом формате, разделяя столбы символом «;» или «,», таким образом позволяя дальнейший импорт в какой-либо табличный редактор или подключение данного файла в какое-либо web или desktop приложение.
Экспортируя таблицу в HTML, программа генерирует код разметки, который затем сохраняется в html файл. В будущем планируется расширить обычный экспорт отдельных таблиц в создание web-сервера, который позволит так же взаимодействовать с базой данных, но уже через браузер или удаленно с любого устройства, на котором установлен современный браузер и есть доступ в интернет.
Рисунок 6 - Сохраненная в HTML таблица.
Рисунок 7 - Диалоговое окно журнала.
Для удобной отладки программы с большой кодовой базой необходимо вести журнал, чтобы в ситуации, когда ошибка возникает во время выполнения как можно быстрее сузить круг поиска той части, где может быть ошибка.
3. Реализация и программирование
Язык С++ не входит в число простых и быстро изучаемых языков программирования, но освоив его даже на среднем уровне программисту становится проще понимать, как работает тот или иной более высокоуровневый код на других языках.
Основной интерфейс был создан в графическом редакторе Qt. Основные элементы:
Панель меню
Виджет дерева (TreeWidget)
Виджет вкладок (TabWidget)
Разделитель между виджетом дерева и виджетом вкладок (Splitter)
Рисунок 8 - Редактор интерфейса.
Другие же окна, вроде окна ввода SQL запроса или окна подключения были реализованы непосредственно через код. Qt предоставляет набор классов для создания диалоговых окон, в некоторых случаях удобнее описать окно при помощи программного кода, чем создавать его в редакторе.
Основной класс, вызываемый при запуске программы, называется SQLManager. Он содержит в себе минимальный набор методов для создания и взаимодействия программы и пользователя с сервером базы данных. При программировании приложения была произведена попытка добиться некоторого высокого уровня абстрактности, чтобы основной код не был перегружен, а заголовочные файлы и файлы реализации не превышали тысячи строк.
Таким образом, следуя вышесказанным требованиям был создан отдельный класс SQLDatabaseBuilder, представляющий собой основную реализацию всех процессов взаимодействия, начиная от подключения к серверу и отображением данных на виджетах окна, заканчивая выполнением запросов и редактированием таблиц.
voidSQLManager::connect_mysql(constQString& host, constQString& name, constQString& password, constQString& port) {
try {
dataBaseBuilder
.setDatabase("QMYSQL", host, name, password, port)
.setTreeWidget(ui.treeWidget)
.setTabWidget(ui.tabWidget)
.run();
} catch (const std::exception& ex) {
QMessageBox::critical({}, "Error", ex.what());
}
}
Листинг 1 - Код инициализации объекта SqlDatabaseBuilder.
Для того, чтобы инициализировать объект базы данных, достаточно указать тип подключения, адрес, имя пользователя, пароль и порт. Для вывода данных на элементы диалогового окна предусмотрены методы setTreeWidget и setTabWidget. Первый представляет собой передачу указателя на виджет дерева, куда после подключения будет отображен список баз данных и таблиц в древовидной структуре, второй принимает указатель на виджет вкладок, куда в дальнейшем будут загружаться таблицы для отображения и редактирования. Метод run, как видно из названия выполняет команду подключения к серверу и заполняет виджет дерева, добавляя в него название подключения, базы данных и таблицы.
Рисунок 9 - Диалоговое окно аутентификации.
Для получения данных о подключении используется диалоговое окно аутентификации, которое предлагает пользователю указать всю необходимую информацию.
void LoginDialog::ok_clicked() {
emit btn_clicked(
host_lineedit->text(),
username_lineedit->text(),
password_lineedit->text(),
port_lineedit->text()
);
host_lineedit->clear();
username_lineedit->clear();
password_lineedit->clear();
port_lineedit->setText("3306");
dialog->close();
}
Листинг 2 - Код слота, передающий данные о подключении.
При нажатии кнопки ОК вызывается сигнал, который связан с действием, которое в свою очередь передает информацию в метод подключения к базе данных. Таким образом происходит установка соединения и отображения списка баз данных.
SqlDatabaseBuilder& SqlDatabaseBuilder::run() {
try {
logger.writeToLog("Attempting to connect to database");
if (database_connection.isOpen()) throw std::exception("Database is aleady opened");
connectDatabase(connectionType_, hostName_, userName_, password_, port_);
} catch (const std::exception& ex) {
logger.writeToLog("Catched exception: "+QString(ex.what()));
throw;
}
try {
logger.writeToLog("Trying to setup TreeWidget");
setupTreeWidget();
} catch (const std::exception& ex) {
logger.writeToLog("Catched exception: "+QString(ex.what())); throw;
}
return *this;
}
Листинг 3 - Код метода запуска.
Внутри реализации класса SQLDatabaseBuilder каждый метод может вызвать исключение при наличии ошибки. Таким образом реализована обработка исключающих ситуаций во всей программе. После того, как исключение будет вызвано, программа выводит диалоговое окно с конкретной ошибкой.
voidSqlDatabaseBuilder::deleteRowFromTable(inttabIndex, QModelIndexListrowIndexes){
QString tabName = openedTabsData.at(tabIndex).at(1);
QString databaseName = openedTabsData.at(tabIndex).at(2);
logger.writeToLog("Removing rows from table "+ tabName);
QSet<int> indexes;
foreach(constQModelIndex& index, rowIndexes) { indexes.insert(index.row()); }
QSqlQuery deleteQuery(database_connection);
database_connection.setDatabaseName(databaseName);
logger.writeToLog("Trying to open database "+ databaseName);
try { openDb(); }
catch (const std::exception& ex) { throw; }
QSqlTableModel* tableModel = newQSqlTableModel;
tableModel->setTable(tabName);
tableModel->select();
foreach(int index, indexes) {
logger.writeToLog("Creating delete query");
QString queryText = QString("delete from %1.%2 where ")
.arg(openedTabsData.at(tabIndex).at(2))
.arg(tabName);
for (int i = 0; i != tableModel->columnCount(); ++i) {
queryText.append(tableModel->headerData(i, Qt::Horizontal).toString()
+"="+"\'"+ tableModel->data(tableModel->index(index, i)).toString() +"\'"
+ (i == tableModel->columnCount() - 1 ? "" : " and "));
}
deleteQuery.exec(queryText);
}
updateTable(tabIndex);
}
Листинг 4 - Код метода удаления строк из таблицы.
При разработке элементов управления был разработан метод удаления строк из таблицы. Так как MYSQL не поддерживает прямого удаления строк по индексу. Один из вариантов удаления строки или строк при помощи запроса можно обратиться к первичному ключу, но лишь при его наличии, если счетчика или первичного ключа нет, то можно реализовать вложенный запрос, который создаст столбец со счетчиком.
В данной же программе удаление идет по индексу, генерируя запрос DELETE, который удаляет строку по полному совпадению, сравнивая все поля. Таким образом в программе достигается удаление сразу множества строк просто выделив их.
ЗАКЛЮЧЕНИЕ
Работа с базами данных занимает значительную часть в жизни программиста большинства направлений, будь то разработчик оконных приложений для Windows или WEB – разработчик на Java.
В приложении SQL Manager главной целью было – получить навыки создания приложения для работы с базами данных, проектирования интерфейса, структуры приложения, а также реализации тех возможностей, которые в будущем обширно бы использовались как создателем приложения, так и другими пользователями.
На данном этапе программа представляет лишь каркас – базовый набор, которого достаточно, чтобы на его основе сконструировать мощное решение с обширными возможностями для будущего пользователя.
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. Макс Шлее. Qt 5.10 Профессиональное программирование на C++. — СПб.: БХВ-Петербург, 2018. — С. 1072. — ISBN 978-5-9775-3678-3.
2. Введение в СУБД MySQL [Электронный ресурс] / . — 2-е изд. — Электрон. текстовые данные. — М. : Интернет-Университет Информационных Технологий (ИНТУИТ), 2016. — 228 c. — 2227-8397. — Режим доступа: http://www.iprbookshop.ru/73650.html