Использование БД SQL. Шаблон проектирования «Фасад» (Facade) [C++, Qt]
Если Вы совсем не знаете что такое SQL — то перед чтением первой части статьи стоит посмотреть соответствующую литературу (ссылки внизу страницы). Первая и третья части рассчитаны на читателя, который уже знает что такое база данных, но хочет узнать как их использовать в своих программах, написанных на С++ с использованием библиотеки Qt.
Паттерн Facade является одним из наиболее часто используемых — многие применяют его, но не знают об этом. Графический интерфейс приложения из третьей части статьи создаётся с помощью Qt Designer, если Вы никогда им пользовались — ознакомьтесь с моей предыдущей статьей.
1 Библиотека Qt и базы данных
Некоторые товарищи уверены, что базы данных нужны только в крупных проектах, однако, это не так. Вы, например, можете использовать БД, уже в том случае, если необходимо сохранять какую-либо информацию между запусками программы (вместо .ini файлов и т.п.). Использование БД более безопасно и надёжно, чем написание своих велосипедов.
Библиотека Qt позволяет работать с различными базами данных, в том числе Oracle DB, MySQL, SQLite. В примерах статьи описана работа с SQLite, однако, от работы с другими базами она отличается разве что подключением (ну и сами базы, конечно, тоже отличаются, но Qt тут не причём). Если содержимое раздела будет не совсем понятно — не расстраивайтесь, будет третий раздел и будут примеры, которые помогут разобраться.
Для работы с SQL в Qt необходимо подключить соответствующий модуль, добавлением в файл проекта следующей строки:
Кроме того, необходимо подключить заголовочный файл для работы с SQL:
Перед началом работы с базой необходимо активизировать соответствующий драйвер, это делается статическим методом QSqlDatabase::addDatabase, который принимает строку, определяющую тип базы (QSQLITE/QMYSQL/…). Полученный методом QSqlDatabase::addDatabase экземпляр класса QSqlDatabase, используется для подключения к базе данных, при этом должно быть установлено имя базы (методом QSqlDatabase::setDatabaseName), а также, возможно хост (QSqlDatabase::setHostName), имя пользователя (QSqlDatabase::setUserName), пароль (QSqlDatabase::setPassword). Подключение к базе данных осуществляется вызовом метода QSqlDatabase::open.
После подключения, с базой возможно взаимодействовать посредством запросов. Передача запроса базе осуществляется методом exec класса QSqlQuery, экземпляр которого должен быть связан с нашей базой (если база одна, связывание произойдёт автоматически). Результатом запроса может быть набор строк, для навигации по которому используются такие методы класса QSqlQuery, как QSqlQuery::first, QSqlQuery::next.
Для получения информации о строке-результате запроса можно использовать экземпляр класса QSqlRecord, который инициализируется посредством вызова QSqlQuery::record. QSqlRecord позволяет получить индекс столбца по имени этого столбца, и чаще всего используется совместно с вызовом QSqlQuery::value для получения значения, хранимого столбцом строки-результата запроса.
Описанными выше средствами мы можем создать базу данных, передать ей запросы на создание таблиц, добавление и выборку данных, в принципе, этого достаточно для нормальной работы с БД. Результаты выборок удобно выводить в таблицу, мы можем сделать это руками, написав цикл с QSqlQuery::next(), однако в Qt есть более удобные, встроенные средства.
Для вывода данных таблицы на экран можно использовать класс QSqlTableModel, экземпляр которого необходимо связать с нашей таблицей базы данных методом QSqlTableModel::setTable. Вызов метода QSqlTableModel::select обновляет данные в модели (используется в случае, если данные в базе были изменены). За отображение таблиц в Qt отвечает класс QTableView, объект которого необходимо связать с моделью при помощи метода QTableView::setModel. Разместить объект QTableView на форме можно при помощи мыши, используя Qt Designer.
QTableView в связке с QSqlTableModel позволяет не только отображать данные базы данных, но и изменять их. Класс QSqlTableModel поддерживает несколько стратегий изменения, которые можно установить вызовом метода QSqlTableModel::setEditStrategy(). Работа с QSqlTableModel будет показана на примере в третьей части статьи.
QTableView может отображать не только таблицы базы данных, но и результаты отдельных выборок (выполнения запросов SELECT), для этого применяется класс QSqlQueryModel. Запрос SELECT передаётся в метод QSqlQueryModel::setQuery, а затем, модель отображается на QTableView вызовом QTableView::setModel.
2 Шаблон проектирования Facade («Фасад»)
Программа, чуть сложнее «hello world«, чаще всего, может быть разделена на подсистемы с фиксированным интерфейсом, т.е. набором функций, предоставляемым подсистемой. Паттерн Facade, как раз, и выступает в роли такого интерфейса, инкапсулируя данные подсистемы и задавая отношения между ее элементами.
Объект Фасад предоставляет упрощённый высокоуровневый интерфейс подсистемы, за счёт использования которого сокращаются зависимости между подсистемами.
Прочитав первую часть статьи, можно заметить, что для работы с базами данных в Qt используется множество объектов. Не очень хочется видеть все эти объекты в основной части программы, их нужно скрыть, для этого можно использовать Фасад. При этом, в программе появится экземпляр класса Facade, который будет предоставлять интерфейс для работы с подсистемой, мы не сможем больше обращаться к элементам подсистемы напрямую, поэтому интерфейс должен быть хорошо продуман. Схематично суть паттерна Facade показана на рис. 1.

рис. 1 использование паттерна Facade
Фасад — очень удобный и простой шаблон проектирования, который постоянно используется всеми без исключения программистами. Facade позволяет:
- минимизировать зависимости между подсистемами;
- минимизировать количество объектов, с которыми приходится взаимодействовать клиентам (упрощает работу с системой);
- предоставить простой интерфейс к сложной системе, которого будет достаточно большинству других систем. Если кому-то не достаточно возможностей такого интерфейса — всегда можно напрямую использовать то, что спрятано за фасад (т.е. фасад ничего не сломал, однако лезть за него не желательно).
В нашем примере, за фасад можно спрятать все, что связано с работой с базой данных, работу со строками таблицы и отдельными ячейками. Это очень удобно.
3 Пример использования баз данных на C++
В качестве примера рассмотрим базу данных вагонов. Каждый вагон имеет номер и фиксированное количество мест. В базе хранится информация обо всех вагонах и о проданных местах. Каждое место связано с каким-либо вагоном
Интерфейс описываемого приложения показан на рис. 2, он содержит:
- 2 экземпляра QTableView (carView,seatView);
- 4 экземпляра QLineedit (carID, carNSeats<количество мест в вагоне>, carNSeats<идентификатор вагона в таблице мест>, carNSeats<номер места>);
- 4 экземпляра QPushButton (carRem,carAdd,seatAdd,seatRem).
В основной части программы есть лишь 2 объекта — первый для отображения графического интерфейса, созданного в Qt Designer, второй — экземпляр класса Фасада. Исходный код заголовочного файла главного окна приведен в листинг 1.
Очевидно, класс DBFacade должен позволять добавлять и удалять информацию из базы. При обновлении информации, должно изменяться отображаемое содержимое таблиц, поэтому экземпляры QTableView должны быть связаны с моделями, инкапсулированными Фасадом.
На листинг 2 приведена реализация методов класса MainUI, как видно, мы не работаем напрямую с базой данных. Большая часть кода выполняет простейшую проверку корректности входных данных (наличие данных в полях ввода).
Видно, что конструктору класса DBFacade передаются указатели на QTableView, это делается для того, чтобы связать их с моделями таблиц БД, хранимыми за Фасадом.
Заголовочный файл класса Фасада приведен на листинг 3.
Как видно, фасад предоставляет интерфейс к базе данных, позволяющий добавлять и удалять данные в таблицы. При выполнении запросов DBFacade должен проверять их корректность, например, при добавлении посадочного места необходимо проверить его наличие (должен существовать соответствующий вагон, и вагон должен иметь достаточное количество сидений.
Исходный код реализации методов класса DBFacade приведен на листинг 4 без пояснений, если есть вопросы — задавайте в комментариях.
Исходный код примера как всегда можно скачать: Работа с БД[Qt] (исходный код).
Программирование на C, C# и Java
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
ОСТОРОЖНО МОШЕННИКИ! В последнее время в социальных сетях участились случаи предложения помощи в написании программ от лиц, прикрывающихся сайтом vscode.ru. Мы никогда не пишем первыми и не размещаем никакие материалы в посторонних группах ВК. Для связи с нами используйте исключительно эти контакты: vscoderu@yandex.ru, https://vk.com/vscode
Вывод данных в DataGridView из БД на C#
В Windows Forms табличные данные удобно выводить с помощью элемента управления DataGridView. В статье покажем, как это сделать на примере таблицы из базы данных и языка программирования C#.
Исходные данные для DataGridView
Имеется таблица в базе данных Microsoft SQL Server с информацией о факультетах университета.

Эти данные будем выводить в элемент DataGridView, расположенный на Windows Forms.
Программа с DataGridView
Создадим новый проект Windows Forms на Visual C# в Visual Studio. На форме расположим элемент управления DataGridView — для этого перетащите его с «Панели элементов» на макет окна.
Добавим в DataGridView три столбца под три соответствующих колонки из таблицы БД с помощью кнопки «Правка столбцов».

Для наглядности назовём их так же, как называются поля в таблице базы данных.

Переходим к написанию кода. Вся функциональность по чтению данных из БД и их последующему выводу в DataGridView реализована в методе LoadData().
ASP.NET SQL подключение через ADO.NET
ADO.NET — это базовая технология доступа к данным. Основной целью технологии является снижение объемов кодирования и обслуживания. ADO.NET предоставляет самый прямой способ доступа к источникам данным MS SQL Server. Веб приложения ASP.NET используют возможности ADO.NET для соединения базами: для получения, обработки и обновления имеющихся в них данных.
Драйверы доступа к базам MS SQL Server
Сайты, построенные на платформе ASP.NET имеют возможность выбора нескольких драйверов (способов) работы с базой данных SQL Server посредством технологии ADO.NET. Для этого используются драйверы для объектно-реляционного сопоставления и драйверы объектно-реляционного доступа.
Драйверы объектно-реляционного сопоставления возвращают данные таблиц базы данных в виде экземпляров классов и их свойств. Примером драйверов объектно-ориентированного доступа к базам данных являются Entity Framework и Entity Framework Core.
Классические драйверы SQL для реляционного доступа используют SQL-язык для выполнения команд получения и обновления данных таблиц базы. Примером таких модулей являются System.Data.SqlClient и Microsoft.Data.SqlClient (введение в новый Microsoft.Data.SqlClient ➹).
Поставщик данных Microsoft.Data.SqlClient создан для .NET Framework, .NET Core и .NET Standard с перспективой долгосрочной поддержки. Модуль Microsoft.Data.SqlClient можно установить через диспетчера пакетов NuGet. В конце статьи прикреплен исходник использования данного модуля доступа к данным.
База данных исходника

Получение данных из базы MS SQL
- Подготовка подключения к базе. Выбор способа подключения и создание строки подключения.
- Создание команды получения или обновления данных. SQL команды (запросы) создаются посредством объектов класса SQLCommand.
- Подключение к базе данных и выполнение команд (SQL запросов). Подключение происходит через объект SQLConnection.
- Получение данных. Если выполняется команда SELECT, то ожидается возврат данных в виде таблиц. Получить данные помогают объекты классов SQLDataAdapter и SQLDataReader.
Примеры строк подключения к MS SQL Server
Формирование подключения начинается с создания строки подключения. Строка подключения для SQL представляет собой входную текстовую информацию с указанием имени сервера, названия базы данных, способа проверки подлинности, идентификационные данные пользователя и некоторую служебную информацию.
Пример строки подключения. Проверка подлинности SQL Server:
«Data Source=COMP\SQLEXPRESS_2016;Initial Catalog=DBMSSQL;Persist Security Info=False;Integrated Security=False;User > COMP\SQLEXPRESS_2016 — имя сервера DBMSSQL — название базы данных Persist Security Info=False — запрет сохранения идентификационных данных после открытия соединения. Integrated Security=False — проверка подлинности пользователя SQL сервером pashaSQL2016;Password=1234 — регистрационные данные пользователя на SQL сервере Пример строки подключения. Проверка подлинности Windows:
Data Source=COMP\SQLEXPRESS_2016;Initial Catalog=DBMSSQL;Integrated Security=True COMP\SQLEXPRESS_2016 — имя сервера DBMSSQL — название базы данных Integrated Security=True — проверка подлинности пользователя зарегистрированного в OS Windows
Подключение к SQL Server
Соединение с базой данных MS SQL Server происходит при помощи экземпляра класса SQLConnection. Завершая сеанс связи с сервером баз данных необходимо явно закрывать соединение вызывая методы Close() или Dispose() .
Объект SqlConnection выходя за пределы области видимости не закрывается самостоятельно. Для гарантированного автоматического закрытия соединения рекомендуется использовать оператор using .
Листинг метода подключения и выполнения запросов к базе данных:
Строка подключения к MS SQL Server создана с помощью построителя, объекта класса SqlConnectionStringBuilder входящего в состав пространства имён Microsoft.Data.SqlClient. Таким способом создаётся синтактически правильная строка подключения к серверу базы данных. Подробнее о создании строки подключения к серверу баз данных можно прочитать в статье о взаимодействии с базой данных через модуль Entity Framework Core.
Объединение подключений — Pooling
Процесс подключения к SQL серверу состоит из нескольких последовательных шагов (установка сетевого соединения, анализ строки подключения, проверка идентификационных данных и др.), каждый из которых занимает некоторое время. При выполнении большого количества запросов к серверу базы данных, общее увеличение времени подключения перед запросами становится заметным и отражается на производительности веб приложения.
Для оптимизации подключений к базе данных технология ADO.NET генерирует пулы соединений. Пул в программировании — это набор ресурсов первоначально сформированных и хранящихся готовыми к использованию. При первом подключении создаётся готовая конфигурация соединения с сервером. После закрытия активного соединения объект подключения не уничтожается, а помещается в пул. При следующем подключении к серверу базы данных используется уже готовое соединение хранящееся в пуле. ADO.NET может создавать несколько пулов. В каждом пуле, в состоянии ожидания, могут храниться несколько десятков неактивных сформированных соединений. В одном пуле могут хранится только одинаковые соединения, имеющие идентичные строки подключения.
В ADO.NET по умолчанию включена поддержка пулов соединений. Если по каким-либо причинам понадобится отключить создание пулов, это можно сделать в строке подключения ключевым словом Pooling со значением False.
Например: «Data Source=PAVEL\SQLEXPRESS_2016;Initial Catalog=DBMSSQL;Integrated Security=True;Pooling=False»
Pooling — сравнение производительности
Для сравнения скорости обработки SQL запросов создадим два одинаковых кода запросов, с оптимизацией подключений и без неё. Две строки запроса к базе: в первой получение данных из одной таблицы, во второй одновременное выполнение сложной команды чтения данных. Для повышения точности измерения каждая команда повторяется установленное количество раз.
Одиночная команда, запрос к одной таблице: строка команды = «SELECT * FROM Sciences»;
Сложная команда, запрос данных в одной строке из нескольких таблиц: строка команды = («SELECT * FROM Sciences;SELECT * FROM Sections;SELECT * FROM SubSections; . . . SELECT * FROM TableN»);
Программный код множественных запросов к базе данных с созданием пулов и без использования оптимизации:
Быстродействие SQLDataReader
Сравнительный тест быстродействия двух вариантов команд с включенным и отключенным пулингом соединений. В первом варианте выполняется команда запроса к одной таблице с повтором 100 раз. Во втором варианте выполняется команда запроса данных у 30 таблиц одновременно, также с повтором 100 раз.
Первое подключение к базе данных:
При первом соединении с базой данных одиночная команда с пулингом выполняется почти в 2 раза дольше, чем с отключёнными пулами. Сложные команды выполняются практически одинаковое время. Логично предположить, что дополнительное время затрачивается на создание пула.
Последующие запросы к базе данных:
Последующие запросы показывают эффективность «пуловой» оптимизации подключений технологии ADO.NET. Одиночные команды выполняются почти в 8 раз быстрее при использовании пулов подключений. В случае со сложными командами эффект использования оптимизации снижается, но всё равно без пулов запросы выполняются медленнее в 1,73 раза.
Быстродействие SQLDataAdapter
Первое подключение к базе данных:
Первое чтение из базы данных с помощью SQLDataAdapter происходит практически аналогично ситуации с SQLDataReader. Но комплексные команды, при включенном пулинге выполняются гораздо быстрее.
Последующие запросы к базе данных:
Последующие запросы одиночных команды выполняются почти в 14 раз быстрее при использовании пулов подключений. Сложные запросы выполняются также чувствительно быстрее с пулингом: в 2,6 раз быстрее подключений без оптимизации. Можно отметить, что SQLDataAdapter обеспечивает гораздо большую скорость выполнения запросов по сравнению с функциональностью SQLDataReader.