Работа с базой данных SQLite
Создание базы данных
Информация о пользовательских настройках может сохраниться с помощью Preference. Однако, для более массивных объемов данных требуются базы данных. Большинство мобильных платформ используют для этого технологию SQLite. Tizen также поддерживает SQLite. SQLite позволяет хранить гораздо меньшие объемы данных, чем, например, Oracle или MS-SQL, однако, имеющегося объема более чем достаточно для мобильных устройств. Команды стандартного языка SQL запускаются после незначительной адаптации к SQLite. Одна БД содержит одну или несколько таблиц. В данном примере мы разработаем пример зачетной ведомости с помощью SQLite.
Создайте новый проект SqliteSample. Откройте файл исходного кода и внесите в него следующие изменения.
#include "sqlitesample.h" #include <sqlite3.h> #include <stdlib.h> typedef struct appdata { Evas_Object *win; Evas_Object *conform; Evas_Object *label; Evas_Object *entry1; Evas_Object *entry2; Evas_Object *entry3; Evas_Object *list; sqlite3 *db; // Database handle char *current_key; } appdata_s; typedef struct recdata { char key[10]; char name[255]; char english[10]; char math[10]; } recdata_s; appdata_s *m_ad;
sqlite3.h — это заголовочный файл библиотеки для работы с SQLite.
stdlib.h — это заголовочный файл библиотеки для изменения типа строк или чисел.
Мы добавили три поля ввода к структуре appdata. Теперь мы собираемся ввести имя студента и оценки по английскому и математике. Далее мы будем выводить результаты тестов в метку.
Sqlite3 — это объектная переменная для базы данных.
Мы собираемся хранить первичные ключи выбранных записей в объекте current-key.
Recdata — это структура для хранения данных оценок студента. Одна recdata означает одну запись.
m_ad — это глобальная переменная, созданная для того, чтобы appdata были доступны везде.
Теперь, мы собираемся создать файл базы данных в папке /data, после чего создать таблицу зачетной ведомости. Создайте три функции выше create_base_gui().
static int CreateTable(appdata_s *ad) { char *ErrMsg; char *sql = "CREATE TABLE IF NOT EXISTS ReportCard(KEY INTEGER PRIMARY KEY, NAME TEXT NOT NULL, ENGLISH INT NOT NULL, MATH INT NOT NULL);"; int ret = sqlite3_exec(ad->db, sql, NULL, 0, &ErrMsg); return ret; } static void init_db(appdata_s *ad) { sqlite3_shutdown(); sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_initialize(); char * resource = app_get_data_path(); int siz = strlen(resource) + 10; char * path = malloc(sizeof(char)*siz); strncat(path, resource, siz); strncat(path, "test.db", siz); sqlite3_open(path, &ad->db); free(path); CreateTable(ad); } static void my_table_pack(Evas_Object *table, Evas_Object *child, int x, int y, int w, int h) { evas_object_size_hint_align_set(child, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(child, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_table_pack(table, child, x, y, w, h); evas_object_show(child); }
CreateTable() - это функция, которая создает таблицу с помощью запроса SQL ('CREATE TABLE').
'IF NOT EXISTS' — это команда для создания таблицы в случае ее отсутствия. В нашем случае имя таблицы ReportCard.
'KEY INTEGER PRIMARY KEY' — это код добавления первичного ключа. Тип данных — целое число с автоинкрементном.
'NAME TEXT NOT NULL' — это код добавления столбца, предназначенного для хранения имени, не может быть пустым. Тип данных - текстовый.
'ENGLISH INT NOT NULL' — это код добавления столбца, предназначенного для хранения отметок по английскому, не может быть пустым. Тип данных - целочисленный.
'MATH INT NOT NULL' — это код добавления столбца, предназначенного для хранения отметок по математике, не может быть пустым. Тип данных - целочисленный.
sqlite3_exec(sqlite3*, char *, int(*callback), void *, char **) - это API, который запускает выполнение запросов SQL. Его параметры следующие: объект SQLite, имя вызывающей функции, пользовательские данные и возврат сообщения об ошибке.
init_db() - это функция, которая создает файл базы данных.
sqlite3_shutdown() - это API, который закрывает базу данных.
sqlite3_config(int, ...) - это API, который определяет свойства базы данных.
Передача SQLITE_CONFIG_URI сохраняет данные в файле базы данных.
sqlite3_initialize() - это API, который инициализирует базу данных.
app_get_data_path() - это API, который возвращает абсолютный путь к папке /data. Папка /res не позволяет запись, так что мы создаем файл базы данных в папке /data.
strncat(char *, char *, size_t) — это API, который добавляет новую строку в конец строки за счет определения максимальной длины строки. Его параметры следующие: оригинальный массив строк, строковые данные, требующие загрузки и максимальная длина.
sqlite3_open(char *, sqlite3 **) - это API, который открывает файл базы данных. Вам необходимо создать файл базы данных, если он еще не существует. Указание пути к файлу в качестве первого параметра заставляет второй параметр возвращать объект базы данных.
my_table_pack() - это функция, которая добавляет элементы управления к Table.
Мы собираемся сделать так, чтобы файл базы данных автоматически открывался при запуске приложения. Разместите вызов описанной выше функции в конец функции create_base_gui() .
/* Show window after base gui is set up */ evas_object_show(ad>win); init_db(ad); }
Добавление новых записей
Далее, мы реализуем возможность добавления новых записей в базу данных при вводе имени студента и его оценок по английскому и математике в поля ввода и нажатии кнопки. Добавьте код в функцию create_base_gui(). Аннотируйте элементы управления.
/* Conformant */ Labelad->conform = elm_conformant_add(ad->win); elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW); elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE); evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(ad->win, ad->conform); evas_object_show(ad->conform); { /* Box to put the table in so we can bottom-align the table * window will stretch all resize object content to win size */ Evas_Object *box = elm_box_add(ad->conform); evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, 0.0); elm_object_content_set(ad->conform, box); evas_object_show(box); /* Table */ Evas_Object *table = elm_table_add(ad->conform); /* Make table homogenous - every cell will be the same size */ elm_table_homogeneous_set(table, EINA_TRUE); /* Set padding of 10 pixels multiplied by scale factor of UI */ elm_table_padding_set(table, 10 * elm_config_scale_get(), 10 * elm_config_scale_get()); /* Let the table child allocation area expand within in the box */ evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); /* Set table to fiill width but align to bottom of box */ evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_box_pack_end(box, table); evas_object_show(table); { /* Bg-1 */ Evas_Object *bg = elm_bg_add(ad->conform); elm_bg_color_set(bg, 210, 210, 210); my_table_pack(table, bg, 0, 0, 1, 1); /* Entry-1 */ ad->entry1 = elm_entry_add(ad->conform); elm_object_part_text_set(ad->entry1, "elm.guide", "Name"); my_table_pack(table, ad->entry1, 0, 0, 1, 1); /* Bg-2 */ bg = elm_bg_add(ad->conform); elm_bg_color_set(bg, 210, 210, 210); my_table_pack(table, bg, 1, 0, 1, 1); /* Entry-2 */ ad->entry2 = elm_entry_add(ad->conform); elm_object_part_text_set(ad->entry2, "elm.guide", "English"); my_table_pack(table, ad->entry2, 1, 0, 1, 1); /* Bg-3 */ bg = elm_bg_add(ad->conform); elm_bg_color_set(bg, 210, 210, 210); my_table_pack(table, bg, 2, 0, 1, 1); /* Entry-3 */ ad->entry3 = elm_entry_add(ad->conform); elm_object_part_text_set(ad->entry3, "elm.guide", "Math"); my_table_pack(table, ad->entry3, 2, 0, 1, 1); /* Button-Add */ Evas_Object *btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Add"); evas_object_smart_callback_add(btn, "clicked", btn_add_cb, ad); my_table_pack(table, btn, 0, 1, 1, 1); } } /* Show window after base gui is set up */ evas_object_show(ad->win);
Мы добавили один контейнер Box, один объект Table, три объекта Bg, и три поля ввода и кнопку. Мы реализуем возможность добавления данных при нажатии на кнопку. Добавьте две функции выше create_base_gui().
static int InsertRecord(appdata_s *ad, unsigned char *name, int english, int math) { char sql[256]; char *ErrMsg; snprintf(sql, 256, "INSERT INTO ReportCard VALUES(NULL,\'%s\',%d,%d);", name, english, math); int ret = sqlite3_exec(ad->db, sql, NULL, 0, &ErrMsg); return ret; } static void btn_add_cb(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; char* s_name = elm_object_text_get(ad->entry1); char* s_english = elm_object_text_get(ad->entry2); int n_english = atoi (s_english); char* s_math = elm_object_text_get(ad->entry3); int n_math = atoi (s_math); InsertRecord(ad, s_name, n_english, n_math); }
InsertRecord() - это функция, которая добавляет новые записи, используя запросы SQL. В нашем случае использовалась команда 'INSERT INTO ReportCard'.
Передайте данные записей в VALUES(). Первичный ключ добавляется автоматически.
При нажатии на кнопку, срабатывает функция btn_add_cb(), которая запрашивает данные из полей ввода и сохраняет их в базе данных.
Запустите пример. Введите имя студента и его оценки и нажмите на кнопку. Пока мы не можем убедиться, что данные были сохранены в базе данных.
Считывание данных, сохраненных в базе данных
далее мы реализуем возможность вывода данных, сохраненных в базе данных и отображения их на экране. Для этого нам потребуется добавить код создания списка в функцию create_base_gui().
static void create_base_gui(appdata_s *ad) { m_ad = ad; ~ /* Button-Add */ Evas_Object *btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Add"); evas_object_smart_callback_add(btn, "clicked", btn_add_cb, ad); my_table_pack(table, btn, 0, 1, 1, 1); /* List */ ad->list = elm_list_add(ad->conform); elm_list_mode_set(ad->list, ELM_LIST_COMPRESS); elm_list_go(ad->list); my_table_pack(table, ad->list, 0, 2, 3, 8); } } /* Show window after base gui is set up */ evas_object_show(ad->win);
Объекты objects сохраненные в глобальных переменных, должны быть доступны везде. Добавьте две новые функции. Эти функции должны быть размещены выше функции init_db() и btn_add_cb().
static int db_read_cb(void *counter, int argc, char **argv, char **azColName) { char buf[255]; recdata_s* rd = malloc(sizeof(recdata_s)); strcpy(rd->key, argv[0]); strcpy(rd->name, argv[1]); strcpy(rd->english, argv[2]); strcpy(rd->math, argv[3]); sprintf(buf, "%s / %s / %s / %s", argv[0], argv[1], argv[2], argv[3]); elm_list_item_append(m_ad->list, buf, NULL, NULL, NULL, (void*)rd); elm_list_go(m_ad->list); return 0; } static int read_db(appdata_s *ad) { char *sql = "select * from ReportCard"; int counter=0; char *ErrMsg; elm_list_clear(ad->list); int ret = sqlite3_exec(ad->db, sql, db_read_cb, &counter, &ErrMsg); return ret; }
Индивидуальная информация о записях будет передаваться в вызывающую функцию при чтении множества записей.
db_read_cb() - это функция вызова, которая получает запись и обрабатывает ее. Эти данные передаются третьему параметру в виде массива.
Создайте структуру recdata_s, сохраните данные для каждого поля и соберите все данные в одну строку. Затем, добавьте строку к списку в виде нового элемента.
read_db() - это функция, которая считывает все данные, сохраненные в базе данных и выдает их в контроле список.
'select * from ReportCard' — это запрос, который возвращает все данные, сохраненные в таблице ReportCard.
Вызовите функцию, описанную выше при запуске приложения и нажатии на кнопку. Добавьте код в конце функций init_db() и btn_add_cb().
static void init_db(appdata_s *ad) { ~ CreateTable(ad); read_db(ad); } static void btn_add_cb(void *data, Evas_Object *obj, void *event_info) { ~ InsertRecord(ad, s_name, n_english, n_math); read_db(ad); }
Это — причина почему функция read_db() должна быть размещена выше функций init_db() и btn_add_cb().
Запустите приложение снова. Только что введенные данные будут отображаться в списке. Введите новые данные и нажмите на кнопку. Вы увидите в списке новые элементы.
Модификация данных
Теперь мы поговорим о том, как модифицировать сохранные данные. Когда пользователь выбирает элемент из списка, данные появляются в полях ввода и их можно редактировать. В функции db_read_cb() задайте имя вызывающей функции к коду добавления нового элемента. Затем создайте вызывающую функцию.
static void list_item_clicked(void *data, Evas_Object *obj, void *event_info) { recdata_s* rd = (recdata_s*)data; m_ad->current_key = rd->key; elm_object_text_set(m_ad->entry1, rd->name); elm_object_text_set(m_ad->entry2, rd->english); elm_object_text_set(m_ad->entry3, rd->math); } static int db_read_cb(void *counter, int argc, char **argv, char **azColName) { char buf[255]; recdata_s* rd = malloc(sizeof(recdata_s)); strcpy(rd->key, argv[0]); strcpy(rd->name, argv[1]); strcpy(rd->english, argv[2]); strcpy(rd->math, argv[3]); sprintf(buf, "%s / %s / %s / %s", argv[0], argv[1], argv[2], argv[3]); elm_list_item_append(m_ad->list, buf, NULL, NULL, list_item_clicked, (void*)rd); //elm_list_item_append(m_ad->list, buf, NULL, NULL, NULL, (void*)rd); elm_list_go(m_ad->list); return 0; }
list_item_clicked() - это функция вызова события выбора элемента з списка.
Сохраните значения первичных ключей в глобальных переменных и выведите другие данные в поля ввода. Код для добавления элементов в список модифицирован в функции db_read_cb(). Задано имя функции вызова выбора элемента. Function. Далее, добавьте код создания новой кнопки в функции create_base_gui().
/* Button-Add */ Evas_Object *btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Add"); evas_object_smart_callback_add(btn, "clicked", btn_add_cb, ad); my_table_pack(table, btn, 0, 1, 1, 1); /* Button-Update */ btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Update"); evas_object_smart_callback_add(btn, "clicked", btn_update_cb, ad); my_table_pack(table, btn, 1, 1, 1, 1); /* List */ ad->list = elm_list_add(ad->conform); elm_list_mode_set(ad->list, ELM_LIST_COMPRESS); elm_list_go(ad->list); my_table_pack(table, ad->list, 0, 2, 3, 8);
Мы реализуем возможность модификации данных выбранного в настоящий момент элемента при нажатии на кнопку. Добавьте две кнопки выше create_base_gui().
static int UpdateRecord(appdata_s *ad, unsigned char *name, unsigned char *english, unsigned char *math) { char sql[256]; char *ErrMsg; snprintf(sql, 256, "UPDATE ReportCard SET NAME=\'%s\', ENGLISH=\'%s\', MATH=\'%s\' WHERE KEY=\'%s\';", name, english, math, ad->current_key); int ret = sqlite3_exec(ad->db, sql, NULL, 0, &ErrMsg); return ret; } static void btn_update_cb(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; char* s_name = elm_object_text_get(ad->entry1); char* s_english = elm_object_text_get(ad->entry2); char* s_math = elm_object_text_get(ad->entry3); UpdateRecord(ad, s_name, s_english, s_math); read_db(ad); }
UpdateRecord() - это функция, которая модифицирует данные, сохраненные в базе данных.
Запрос 'UPDATE ReportCard' модифицирует данные, сохраненные в таблице ReportCard .
Выражение SET NAME=\’%s\’ сохраняет строковые данные в столбце NAME.
Выражение WHERE KEY=\’%s\’ модифицирует записи с одинаковыми первичными ключами.
btn_update_cb() - это функция, которая сохраняет данные, введенные в поле ввода для текущей выбранной записи.
Запустите пример.
Удаление записей
Мы реализуем возможность удаления выбранных записей при нажатии на кнопку. Добавим код создания третей кнопки в функции create_base_gui().
/* Button-Update */ btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Update"); evas_object_smart_callback_add(btn, "clicked", btn_update_cb, ad); my_table_pack(table, btn, 1, 1, 1, 1); /* Button-Del */ btn = elm_button_add(ad->conform); elm_object_text_set(btn, "Del"); evas_object_smart_callback_add(btn, "clicked", btn_del_cb, ad); my_table_pack(table, btn, 2, 1, 1, 1); /* List */ ad->list = elm_list_add(ad->conform); elm_list_mode_set(ad->list, ELM_LIST_COMPRESS); elm_list_go(ad->list); my_table_pack(table, ad->list, 0, 2, 3, 8);
Добавим обработчик нажатия кнопки выше функции create_base_gui().
static int DelRecord(appdata_s *ad) { char sql[256]; char *ErrMsg; snprintf(sql, 256, "DELETE FROM ReportCard WHERE KEY=\'%s\';", ad->current_key); int ret = sqlite3_exec(ad->db, sql, NULL, 0, &ErrMsg); return ret; } static void btn_del_cb(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; DelRecord(ad); read_db(ad); }
DelRecord() - это функция, удаляющая записи из базы данных.
Выражение 'DELETE FROM ReportCard' удаляет данные из таблицы ReportCard.
Выражение WHERE KEY=\’%s\’ удаляет записи с совпадающим первичным ключом.
btn_del_cb() - это функция, удаляющая текущую выделенную запись при нажатии на кнопку.
Запустите пример.