Учебный пример о подключении популярного формата базы данных к проекту. Будем показывать на примере Visual Studio 2017 Community.
Содержание
- Создание базы данных
- Создание болванки приложения
- Код открытия файла
- Скачивание библиотеки для работы SQLite
- Подключение библиотеки
- Подключение к базе данных
- Выгрузка данных из базы данных
- Более простой код вывода данных из таблицы
- Вывод данных из базы данных в таблицу
- Расширенный пример с кнопкой добавления записи
- Удаление записи из таблицы
Создание базы данных
По этой статье создайте файл базы данных test.db с одной таблицей students.
Для знающих:
1 2 3 4 5 6 7 8 |
CREATE TABLE `students` ( `_id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `age` INTEGER NOT NULL ); INSERT INTO `students` VALUES (1,'Дмитрий',20); INSERT INTO `students` VALUES (2,'Галя',18); INSERT INTO `students` VALUES (3,'Костя',19); |
Создание болванки приложения
Подготовьте приложение с готовой формой. Прочитать об этом можно тут: Visual Studio 2017, Visual Studio 2015, Visual Studio 2010.
Пока разместим на форме кнопку поле вывода.
Также разместим диалоговое окно открытия файлов.
Код открытия файла
Дважды кликаем по кнопке и переходи в код обработки клика кнопки.
И разместим там такой код. Тут пока только получаем имя файла через диалоговое окно и очищаем текстовое поле.
1 2 3 4 5 6 |
if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { textBox1->Clear(); String^ fileName = openFileDialog1->FileName; } |
Скачивание библиотеки для работы SQLite
К сожалению, в Visual Studio нет встроенной поддержки SQLite (В отличии от того же Qt). По идеи, есть в Visual Studio менеджер пакетов NuGet, который позволяет быстро загружать нужные библиотеки, но нужно понимать, что CLR платформа уходит потихоньку, и Microsoft делает упор на C#. В общем, вы сможете загрухить библиотеку через NuGet, но ничего не скомпилируется.
Поэтому будем делать всё вручную.
Идем на сайт https://system.data.sqlite.org.
Там в раздел Download.
И откроется страница, где куча ссылок на разные версии сборок библиотеки. Что же выбрать? Нам точно нужен файл из раздела Precompiled Binaries.
А вот далее четкого ответа вам не дам. Файлов с пометкой Precompiled Binaries много. Они отличаются разрядностью программы (x86 и x64), версией .NET Framework и версией Visual Studio. Причем последний параметр, как оказалось, самый неважный.
Итак, я пишу пример под Visual Studio 2017 Community под Windows 10 x64.
Но CLR приложение компилируется как 32 разрядное приложение (x86). И через .NET Framework 4.5.1.
В общем, у меня сработала библиотека Precompiled Binaries for 32-bit Windows (.NET Framework 4.5.1). Который вообще-то собран под Visual Studio 2013, а не 2017. У вас ввиду других версий Visual Studio и настроек компилятора может потребоваться другой файл.
Внимание! Для Visual Studio 2015 подошел этот же файл. А для Visaul Studio 2010 подошел файл sqlite-netFx40-binary-bundle-Win32-2010-1.0.105.2.zip из раздела Precompiled Binaries for 32-bit Windows (.NET Framework 4.0).
Скачиваем наш файл.
На всякий случай тут его размещаю. Но лучше скачать с сайта.
sqlite-netFx451-binary-bundle-Win32-2013-1.0.105.1.zip
Распаковываем его.
Там нам нужны будут файлы формата .dll.
Размещаем их в какую-нибудь папку. Я разместил в папку C:\Users\[Пользователь]\Documents\Visual Studio 2017\SQLite, но вы можете разместить и в другом месте, где вам захочется.
Подключение библиотеки
Теперь обратно переходим к проекту в Visual Studio. У проекта есть раздел Ссылок к Обозревателе решений.
Щелкаем правой кнопкой по разделу и выбираем Добавить ссылку….
И там через Обзор… выбираем файл System.Data.SQLite.dll
Если после нажатия на OK выскочило сообщение об ошибке, то скорее всего с сайта вы не тот файл скачали.
У вас в разделе Ссылки должно появится упоминание о библиотеке.
Для Visual Studio 2010 подключение ссылки выглядит по другому.
Теперь мы можем в файле кода формы (у меня это MyForm.h) добавить подключение пространств имен библиотек.
1 2 |
using namespace System::Data::SQLite; using namespace System::Text; |
Вторая строчка к нашей библиотеке не имеет отношения, но функции оттуда нам будут нужны.
Скомпилируйте и запустите программу. Если всё запустилось, то пока вы всё делали правильно.
Подключение к базе данных
Возвращаемся в код кнопки. Там у нас пока прописано открытие файла через диалоговое окно и получение имени выбранного файла.
Пропишем код подключение базы данных внутри условия открытия диалогового окна (не запутайтесь!).
1 2 3 4 5 6 7 8 9 10 11 12 |
SQLiteConnection ^db = gcnew SQLiteConnection(); try { db->ConnectionString = "Data Source=\"" + fileName + "\""; db->Open(); db->Close(); } finally { delete (IDisposable^)db; } |
Скомпилируйте программу и запустите. Проверьте работу кнопки. Если программа не вылетает при выборе файла базы данных test.db, то теперь точно библиотека подключена правильно.
Выгрузка данных из базы данных
Теперь в кнопке мы подключились к базе данных. Попробуем вытащить все данные из таблицы students.
Для этого между строчек db->Open(); и db->Close(); поместим такой код.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// Display Table try { SQLiteCommand ^cmdSelect = db->CreateCommand(); cmdSelect->CommandText = "SELECT * FROM students;"; SQLiteDataReader ^reader = cmdSelect->ExecuteReader(); StringBuilder ^sb = gcnew StringBuilder(); for (int colCtr = 0; colCtr < reader->FieldCount; ++colCtr) { // Add Seperator (If After First Column) if (colCtr > 0) sb->Append("|"); // Add Column Name sb->Append(reader->GetName(colCtr)); } sb->AppendLine(); sb->Append("~~~~~~~~~~~~"); sb->AppendLine(); while (reader->Read()) { for (int colCtr = 0; colCtr < reader->FieldCount; ++colCtr) { // Add Seperator (If After First Column) if (colCtr > 0) sb->Append("|"); // Add Column Text sb->Append(reader->GetValue(colCtr)->ToString()); } sb->AppendLine(); } textBox1->Text = sb->ToString(); } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception While Displaying MyTable ..."); } |
Полный код кнопки получается такой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { textBox1->Clear(); String^ fileName = openFileDialog1->FileName; SQLiteConnection ^db = gcnew SQLiteConnection(); try { db->ConnectionString = "Data Source=\"" + fileName + "\""; db->Open(); // Display Table try { SQLiteCommand ^cmdSelect = db->CreateCommand(); cmdSelect->CommandText = "SELECT * FROM students;"; SQLiteDataReader ^reader = cmdSelect->ExecuteReader(); StringBuilder ^sb = gcnew StringBuilder(); for (int colCtr = 0; colCtr < reader->FieldCount; ++colCtr) { // Add Seperator (If After First Column) if (colCtr > 0) sb->Append("|"); // Add Column Name sb->Append(reader->GetName(colCtr)); } sb->AppendLine(); sb->Append("~~~~~~~~~~~~"); sb->AppendLine(); while (reader->Read()) { for (int colCtr = 0; colCtr < reader->FieldCount; ++colCtr) { // Add Seperator (If After First Column) if (colCtr > 0) sb->Append("|"); // Add Column Text sb->Append(reader->GetValue(colCtr)->ToString()); } sb->AppendLine(); } textBox1->Text = sb->ToString(); } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception While Displaying MyTable ..."); } db->Close(); } finally { delete (IDisposable^)db; } } |
Запускаем приложение, кликаем на кнопку, выбираем файл test.db. И вы должны увидеть что-то такое.
Вот мы и вывели нашу таблицу в текстовое поле.
Более простой код вывода данных из таблицы
Немного упростил код кнопки, убрав несколько сложных для первого знакомства мест.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { textBox1->Clear(); String^ fileName = openFileDialog1->FileName; SQLiteConnection ^db = gcnew SQLiteConnection(); try { db->ConnectionString = "Data Source=\"" + fileName + "\""; db->Open(); try { SQLiteCommand ^cmdSelect = db->CreateCommand(); //Обратите внмиание, что SQL запрос оформляем как обычную строчку cmdSelect->CommandText = "SELECT * FROM students;"; SQLiteDataReader ^reader = cmdSelect->ExecuteReader(); //В sb будем записывать итоговый результат String ^sb = ""; //Пробегаем по каждой записи while (reader->Read()) { //В каждой записи пробегаем по всем столбцам for (int colCtr = 0; colCtr < reader->FieldCount; ++colCtr) { //Добавлем значение столбца в sb sb += reader->GetValue(colCtr)->ToString(); sb += (" | "); } sb += "\r\n"; } //Выводим результат textBox1->Text = sb->ToString(); } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception While Displaying MyTable ..."); } db->Close(); } finally { delete (IDisposable^)db; } } |
Вывод данных из базы данных в таблицу
Мы только что выводили информацию в textBox1. Но это неудобно для вывода таблиц. Выведем в таблицу.
На форме разместил еще одну кнопку и компонент DataGridView.
Вывод данных в таблице будем основывать на статье Вывод таблицы вручную в Visual Studio в CLR приложении.
Будем использовать в коде вектора, так что подключим его через инклуд.
1 |
#include <cliext/vector> |
И через подключение пространства имен.
1 |
using namespace cliext; |
Код клика кнопки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { textBox1->Clear(); String^ fileName = openFileDialog1->FileName; SQLiteConnection ^db = gcnew SQLiteConnection(); try { db->ConnectionString = "Data Source=\"" + fileName + "\""; db->Open(); try { SQLiteCommand ^cmdSelect = db->CreateCommand(); //Обратите внмиание, что SQL запрос оформляем как обычную строчку cmdSelect->CommandText = "SELECT * FROM students;"; SQLiteDataReader ^reader = cmdSelect->ExecuteReader(); //В table будем записывать итоговый результат DataTable ^table; //Невидимая таблица данных DataColumn ^column; //Столбец таблицы DataRow ^row; //Строка таблицы //Создаем таблицу данных table = gcnew DataTable(); //Вектор названий столбцов vector<String^>^ nameColumns = gcnew vector<String^>(); //Заполним данные о столбцах for (int i = 0; i < reader->FieldCount; i++) { nameColumns->push_back(reader->GetName(i)); column = gcnew DataColumn(nameColumns->at(i), String::typeid); table->Columns->Add(column); } //Пробегаем по каждой записи while (reader->Read()) { //Заполняем строчку таблицы row = table->NewRow(); //В каждой записи пробегаем по всем столбцам for (int i = 0; i < reader->FieldCount; i++) { //Добавлем значение столбца в row row[nameColumns->at(i)] = reader->GetValue(i)->ToString(); reader->GetValue(i)->ToString(); } table->Rows->Add(row); } //Выводим результат dataGridView1->DataSource = table; } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception While Displaying MyTable ..."); } db->Close(); } finally { delete (IDisposable^)db; } } |
Расширенный пример с кнопкой добавления записи
Мы показали, как считывать данные из SQLite и выводить в таблицу DataGridView. Но у нас сейчас настроенное соединение SQLiteConnection работает только при клике одной кнопки. И, если нам потребуется обращаться к базе данных в других кнопках, то придется создавать новые подключения. Неудобно. Попробуем исправить это.
Задача: Нам нужно, чтобы при нажатию на еще одну кнопку происходило добавление новой записи в базу данных. Причем в отображаемой таблице также происходили изменения.
Как и в предыдущем примере, нам будут нужны вектора, так что не удаляем подключение cliext.
1 |
#include <cliext/vector> |
1 |
using namespace cliext; |
Сделаем переменную подключения к базе данных SQLiteConnection ^db общей для всей нашей формы.
1 |
SQLiteConnection ^db; |
Нам нужно будет в двух кнопках Открыть базу данных и Добавить данные считывать данные из базы данных. Поэтому вынесем заполнение невидимой таблицы DataTable ^table в отдельный метод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
private: DataTable^ fillDataTable() { DataTable ^table; try { SQLiteCommand ^cmdSelect = db->CreateCommand(); //Обратите внимание, что SQL запрос оформляем как обычную строчку cmdSelect->CommandText = "SELECT * FROM students;"; SQLiteDataReader ^reader = cmdSelect->ExecuteReader(); //Переменные DataColumn ^column; //Столбец таблицы DataRow ^row; //Строка таблицы //Создаем таблицу данных table = gcnew DataTable(); //Вектор названий столбцов vector<String^>^ nameColumns = gcnew vector<String^>(); //Заполним данные о столбцах for (int i = 0; i < reader->FieldCount; i++) { nameColumns->push_back(reader->GetName(i)); column = gcnew DataColumn(nameColumns->at(i), String::typeid); table->Columns->Add(column); } //Пробегаем по каждой записи while (reader->Read()) { //Заполняем строчку таблицы row = table->NewRow(); //В каждой записи пробегаем по всем столбцам for (int i = 0; i < reader->FieldCount; i++) { //Добавляем значение столбца в row row[nameColumns->at(i)] = reader->GetValue(i)->ToString(); reader->GetValue(i)->ToString(); } table->Rows->Add(row); } } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception While Displaying MyTable ..."); } return table; } |
Закрытие соединения с базой данных вынесем в деструктор формы.
1 |
db->Close(); |
Тогда код кнопки открытия базы данных сократится до такого кода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { String^ fileName = openFileDialog1->FileName; db = gcnew SQLiteConnection(); try { db->ConnectionString = "Data Source=\"" + fileName + "\""; db->Open(); DataTable ^table = fillDataTable(); dataGridView1->DataSource = table; } catch (Exception ^e) { MessageBox::Show("Error Working SQL: " + e->ToString(), "Exception ..."); } } |
Тогда код клика кнопки Добавить запись можно определить так.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
try { SQLiteCommand ^cmdInsertValue = db->CreateCommand(); cmdInsertValue->CommandText = "INSERT INTO students (name, age) VALUES('Сандра', 18);"; cmdInsertValue->ExecuteNonQuery(); DataTable ^table = fillDataTable(); dataGridView1->DataSource = table; } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception ..."); } |
Удаление записи из таблицы
Напоследок покажем код кнопки, которая удаляет выделенную запись из таблицы. В этом примере из нового появится определение номера выделенной записи в таблице.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//Номер выделенной строки int index = dataGridView1->CurrentCell->RowIndex; //Определим _id в выделенной строке String^ _id = dataGridView1->Rows[index]->Cells["_id"]->Value->ToString(); try { SQLiteCommand ^cmdInsertValue = db->CreateCommand(); cmdInsertValue->CommandText = "DELETE FROM students WHERE _id = " + _id+ ";"; cmdInsertValue->ExecuteNonQuery(); DataTable ^table = fillDataTable(); dataGridView1->DataSource = table; } catch (Exception ^e) { MessageBox::Show("Error Executing SQL: " + e->ToString(), "Exception ..."); } |
Обратите внимание на то, что при нажатии на кнопки Добавить запись и Удалить запись вначале мы производим изменения в базе данных, а потом заново из базы данных выгружаем данные.
Исходники проекта под Visual Studio 2017 WorkWithSqlite.zip.