Прежде чем приступать к самой библиотеке хочется сказать пару слов о SQLite.
1. SQLite
SQLite — это база данных с открытым исходным кодом, которая поддерживает стандартные возможности SQL, а именно:
- Синтаксис.
- Транзакция.
- Триггеры.
- Параметризированные операторы.
- Агрегатные функции.
Данная БД занимает очень мало места примерно 250 Кбайт.
Каждое значение, хранящееся в базе данных SQLite, имеет один из следующих типов:
- Integer — Значение представляет собой целое число со знаком, сохраненное в 1, 2, 3, 4, 6 или 8 байтах в зависимости от величины значения.
- Real — Значение представляет собой значение с плавающей запятой, которое хранится как 8-байтовое число с плавающей точкой IEEE.
- Text — Значение представляет собой текстовую строку, хранящуюся с использованием кодировки базы данных (UTF-8, UTF-16BE или UTF-16LE)
- Blob — Значение представляет собой блок данных, который хранится точно так же, как он был введен.
При использовании SQLite предоставляется доступ к файловой системе, что может проходить значительно медленно, поэтому в качестве решения данной проблемы необходимо выполнять все операции с БД не в главном потоке (ui потоке), а нужно использовать фоновый поток.
Getting to know the room display
Ограничения SQLite по сравнению с MySQL и PostgreSQL:
- Поддерживаемые типы данных. В SQLite их 4, в то время как в MySQL и PostgreSQL больше 20.
- SQLite не стоит выбирать если вы собираетесь работать над приложением, доступом к БД в которой будут одновременно пользоваться несколько человек. В данном кейсе например лучше выбрать полнофункциональную РСУБД MySQL.
- В SQLite имеются недостатки с операциями записи, потому что данная БД позволяет единовременно исполнять лишь одну операцию записи.
- Отсутствие пользовательского управления, т.е. нет возможности управлять связями в таблицах в соответствии с привилегиями.
- Отсутствие возможности повышения производительности БД за счёт внутренних настроек
2. Что такое Room?
Компоненты архитектуры Android — это набор библиотек, которые помогают создавать надежные, тестируемые и обслуживаемые приложения. Соответственно Room входит в данные компоненты и позволяет упрощать работу с объектами SQLiteDatabase в приложении, уменьшая объем стандартного кода и проверяя запросы SQL во время компиляции.
Room состоит из 3 основных компонентов:
- Entity — объект таблицы БД.
- Dao — предоставляет методы, которое приложение может производить над базой данных.
- Database — содержит базу данных и служит основной точкой доступа для базового подключения к постоянным данным приложения.
Например некоторые методы, которые есть в DAO:
- Query — запрос на получение, обновлению и удалению данных по какому либо условию.
- Insert — добавление объекта в базу данных.
- Delete — удаляет объекты.
- Update — обновляет объекты.
Метод Query работает асинхронно, а остальные 3 являются синхронными.
How to create rooms and build walls — ROOMTODO — Tutorial
Database предоставляет приложению экземпляры DAO, связанных с этой базой данных. В свою очередь, приложение может использовать DAO для извлечения данных из базы данных в качестве экземпляров связанных объектов сущностей данных. Приложение также может использовать определенные объекты данных для обновления строк из соответствующих таблиц или для создания новых строк для вставки. На иллюстрации ниже отображена взаимосвязь между различными компонентами Room.
3. Особенности и подводные камни
4. Практика
В данном разделе будет описано пошаговое руководство по созданию приложения с локальной БД. Начальные данные в БД будут заполняться автоматически при запуске приложения — для такого заполнения будут показаны 2 способа: в коде и из готового файла БД. Также в данном приложении можно будет проводить такие действия над БД как добавление, удаление, так и редактирование записей.
Для реализации приложения будет использован следующий стек технологий:
- RxJava3 — для выполнения запросов к БД в фоновом потоке.
Источник: medium.com
Room: Хранение данных на Android для всех и каждого
2017-08-22 в 18:16, admin , рубрики: android, java, room, разработка мобильных приложений, Разработка под android
Room — это новый способ сохранить данные приложений в Android-приложении, представленный в этом году на Google I/O. Это часть новойAndroid Architecture, группа библиотек от Google, которые поддерживают уместную архитектуру приложений. Room предлагается в качестве альтернативной альтернативы Realm, ORMLite, GreenDao и многим другим.
Room — это высокоуровневый интерфейс для низкоуровневых привязок SQLite, встроенных в Android, о которых вы можете узнать больше в документации. Он выполняет большую часть своей работы во время компиляции, создавая API-интерфейс поверх встроенного SQLite API, поэтому вам не нужно работать с Cursor или ContentResolver.
Использование Room
Во-первых, добавьте Room в свой проект. После этого вам нужно будет передать в Room, как выглядят ваши данные. Предположим, имеется простой класс модели, который выглядит следующим образом:
public class Person
Благодаря этим двум аннотациям Room теперь знает, как создать таблицу для хранения экземпляров Person.
Важная вещь, которую следует учитывать при настройке ваших моделей: каждое поле, которое хранится в базе данных, должно быть общедоступным или иметь геттер и сеттер в стандартном стиле Java Beans (например, getName () и setName (имя строки)).
В классе Person теперь есть вся информация, которая требуется Room для создания таблиц, но у вас нет способа фактически добавлять, запрашивать или удалять данные из базы данных. Вот почему вам нужно будет сделать объект доступа к данным (DAO). DAO предоставляет интерфейс в самой базе данных и занимается манипулированием хранимыми данными Person.
Вот простой интерфейс DAO для класса Person:
Первое, что нужно заметить, это то, что PersonDao — это интерфейс, а не класс. Другая интересная деталь — это инструкции SQL в аннотациях Query (). Операторы SQL говорят Room, какую информацию вы хотите получить из базы данных. Они также проверяются во время компиляции. Поэтому, если вы измените подпись метода List getAllPeopleWithFavoriteColor ( цвет строки ) на List getAllPeopleWithFavoriteColor ( int color ), Room выдаст ошибку во время компиляции:
incompatible types: int cannot be converted to String
И если вы сделаете опечатку в выражении SQL, например, напишите favoriteColors ( множественное число ) вместо favoriteColor ( единственное число ), Room также выдаст ошибку компиляции:
There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: favoriteColors)
Вы не можете получить экземпляр PersonDao, потому что это интерфейс. Чтобы иметь возможность использовать классы DAO, вам необходимо создать класс базы данных. За кулисами этот класс будет отвечать за ведение самой базы данных и предоставление экземпляров DAO.
Вы можете создать свой класс базы данных всего за пару строк:
Это лишь описание структуры базы данных, но сама база данных будет жить в одном файле. Чтобы получить экземпляр AppDatabase, сохраненный в файле с именем populus-database, вы должны написать:
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, «populus-database»).build();
Если вы хотите получить все данные обо всех Person, которые находятся в базе данных, вы могли бы написать:
List everyone = db.getPersonDao().getAllPeople();
Преимущества использования Room
В отличие от большинства ORM, Room использует обработчик аннотации для выполнения всей своей манеры сохранения данных. Это означает, что ни ваши классы приложений, ни классы моделей не должны ничего расширять в Room, в отличие от многих других ORM, включая Realm и SugarORM. Как вы видели при ошибках с аннотациями Query () выше, вы также получаете возможность проверки корректности SQL-запросов во время компиляции, что может сэкономить вам много хлопот.
Room также позволяет вам наблюдать за изменениями данных, интегрируя их как с API LiveData Архитектурных Компонентов, так и с RxJava 2. Это означает, что если у вас сложная схема, где изменения в базе данных должны появляться в нескольких местах вашего приложения, Комната делает уведомления об изменениях. Это мощное дополнение может быть включено с однострочным изменением вашего DAO. Все, что вам нужно сделать, это изменить тип возвращаемых значений.
Например, этот метод:
Самое большое ограничение в Room: взаимосвязи
Самым большим ограничением в Room является то, что он не будет обрабатывать отношения с другими типами сущностей для вас автоматически, как и другие ORM. Это означает, что если вы хотите отслеживать домашних животных:
То Room выдаст ошибку компиляци, так как не знает, как сохранить отношения между Person и Pet:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Ошибка при компиляции предлагает конвертер типов, который преобразует объекты в примитивы, которые могут быть непосредственно сохранены в SQL. Поскольку List нельзя свести к примитиву, вам нужно сделать что-то другое. Это отношения «один ко многим», где у одного Person может быть много Pet.
Room не может моделировать такие отношения, но она может справиться с обратными отношениями — у каждого Pet есть один Person. Чтобы смоделировать это, удалите поле для Pet в Person и добавьте поле ownerId в класс Pet:
Это приведет к тому, что Room обеспечит ограничение внешнего ключа между объектами. Комната не будет вызывать отношения «один-ко-многим» и «много-к-одному», но она дает вам инструменты для выражения этих отношений.
Чтобы получить всех домашних животных, принадлежащих конкретному человеку, вы можете использовать запрос, который находит всех домашних животных с данным идентификатором владельца. Например, вы можете добавить в свой DAO следующий метод:
Стоит ли использовать Room?
Если вы уже настроили сохранение данных в своем приложении и довольны им, вы должны его сохранить. Каждая ORM и встроенная реализация SQLite будут продолжать работать так же, как и раньше. Room — это всего лишь еще один вариант сохранения данных.
Если вы используете SQLite или собираетесь использовать его, вы должны попробовать Room. Он обладает всеми возможностями, необходимыми для выполнения расширенных запросов, одновременно устраняя необходимость писать SQL-запросы для поддержки базы данных самостоятельно.
Источник: www.pvsm.ru
Библиотека «Room» для начинающего Android-разработчика
Здравствуй, дорогой читатель. Каждый Android-разработчик сталкивался (или столкнётся во время своей профессиональной карьеры) с задачей, в которой необходимо хранить большое количество изменяемых данных. В данной статье будет разобрана библиотека от Google — Room.
В статье будет рассказано об основных компонентах библиотеки и будет разобран базовый, не очень сложный пример.
Статья предназначена для новичков, не знакомых с данной библиотекой, но, желательно, имеющих базовые знаниях о SQLite, Kotlin Coroutines, Kotlin Flow, MVVM.
Все материалы и исходный код можно найти здесь.
Теоретическая часть
Для хранения каких-либо данных, Android-разработчику предоставлены следующие способы: Files, SharedPreferences, SQLite, Resources/Assets. Но какой способ выбрать? Для ответа на данный вопрос можно воспользоваться алгоритмом, изображенном на рисунке ниже.
В случае, если данные, которые мы будем хранить, могут изменяться и имеют не простую структуру, следует выбрать SQLite.
SQLite — это реляционная база данных, в которой все данные хранятся в таблицах, которые в свою очередь могут быть связаны между собой. Для взаимодействия с базой данных используется специальный язык запросов — SQL. В случае, если Вы незнакомы с данной реляционной базой данных, — рекомендую данный источник.
Один из способов для работы с SQLite в Android — это, встроенный в Android SDK, SQL API. Данное API позволяет работать с базой данных, но, по-моему мнению, данная технология далеко не простая, а в некоторых моментах даже сложная. В данной статье мы не будем разбирать данную технологию, а сразу перейдем к библиотеке «Room».
Room — это библиотека, представленная на Google I/O в 2017 году. Данная библиотека работает с базой данный SQLite и выполняет большую часть работы за Вас. Все что необходимо разработчику — это «объяснить» библиотеке как выглядят данные, их структуру и способы взаимодействия с помощью специальных аннотаций:
Это далеко не все аннотации, которые предоставляет «Room», но являющиеся основными. Более подробно аннотации будут рассмотрены в практическом примере.
Также следует отметить, что существуют специальные Tuple-классы, которые никак не помечаются, но являются важной частью при разработке. Данные классы используются при взаимодействии с базой данных (например, когда нам необходимо получить какую-то часть данных из таблицы, а не все данные сразу). Более подробно Tuple-классы будут рассмотрены в практическом примере.
Практическая часть
В качестве не сложного примера, создадим приложение, которое будет «имитировать» создание и отображение статистических данных какой-то игры (например, судоку). Приложение будет состоять из двух экранов: первый — заполнение и отправка данных в базу; второй — список со всеми данными из базы.
Статистические данные будут состоять из следующих компонентов: результат игры (победа / поражение), уровень сложности (легкая, сложная и т.д.), количество ошибок, количество набранных очков.
Важное уточнение. В данной статье не будут приведены листинги кода с версткой xml-файлов и всех классов приложения. Полностью готовый проект можно найти здесь.
В первую очередь необходимо указать все зависимости, которые будут использованы приложением. Для этого в файл сборки build.gradle нашего приложения:
dependencies < . implementation ‘androidx.room:room-runtime:2.5.0’ // Библиотека «Room» kapt «androidx.room:room-compiler:2.5.0» // Кодогенератор implementation ‘androidx.room:room-ktx:2.5.0’ // Дополнительно для Kotlin Coroutines, Kotlin Flows >
plugins
android < . defaultConfig < . kapt < arguments > > >
Данные блоки кода подключают нужные библиотеки (первый блок), добавляют нужный плагин (второй блок) и указывают нужный путь к каталогу, который будет хранить схему нашей базы данных (третий блок).
Перед тем как перейти к описанию сущностей, необходимо представить как база данных будет выглядеть, из каких таблиц она будет состоять. В данном практическом примере база данных будет состоять из трех таблиц:
- таблица «difficulty_levels», в которой будут хранится все доступные уровни сложности;
- таблица «results», в которой будут хранится все доступные результаты игры;
- таблица «statistic», в которой будут хранится все статистические данные.
Схематично база данных будет выглядеть следующим образом:
SQL-скрипт для такой базы данных выглядел бы следующим образом:
CREATE TABLE difficulty_levels( id INTEGER PRIMARY KEY, difficulty_name TEXT ); CREATE TABLE results ( id INTEGER PRIMARY KEY, result_name TEXT ); CREATE TABLE statistic ( id INTEGER PRIMARY KEY, result_id INTEGER, difficult_id INTEGER, mistakes INTEGER, points INTEGER, FOREIGN KEY (result_id) REFERENCES results(id), FOREIGN KEY (difficult_id) REFERENCES difficulty_levels(id) );
Примечание: никогда не храните секретные данные (например, пароли) в открытом виде. Всегда хэшируйте их!
После того, как была разобрана схема базы данных, необходимо перейти к созданию сущностей. Создадим data-class DifficultyLevelsDbEntity , который будет описывать таблицу «difficulty_levels»:
Аналогично создадим класс ReultsDbEntity :
Теперь перейдем к созданию более сложной сущности — StatisticDbEntity :
Свойство foreignKeys объявляет составные ключи. В данном случае составных ключа два — result_id и difficult_id. В объекте ForeignKey указываются сущность-родитель (entity), столбец-родитель (parentColumns) и столбец-ребенок (childColumns).
Когда сущности были созданы, dao-интерфейс объявлен необходимо создать абстрактный класс AppDatabase , который будет описывать базу данных:
Версия базы данных используется для контроля базы данных, ее данных и т.д. Зачастую это используется при миграции базы данных с одной версии на другую, но это уже совсем другая история.
В самом классе создан абстрактный метод, который возвращает dao-интерфейс.
Так же создадим tupple-класс StatisticInfoTuple , который будет использоваться при «вытягивании» статистических данных из таблицы. Данный класс очень похож на StatisticDbEntity, но поля, которые хранят результат и уровень сложности, уже имеют тип String, так как там будут хранится значения результата и уровня сложности.
После всех проделанных действий необходимо перейти к реализации dao-интерфейса. В данном интерфейсе создадим три метода, которые будут вставлять новые статистические данные, удалять данные по уникальному значению (id) и получать список всех данных из таблицы statistic:
И последнее, что необходимо сделать с базой данной — создать ее. Для этого выполним следующее:
object Dependencies < private lateinit var applicationContext: Context fun init(context: Context) < applicationContext = context >private val appDatabase: AppDatabase by lazy < Room.databaseBuilder(applicationContext, AppDatabase::class.java, «database.db») .createFromAsset(«room_article.db») .build() >>
В данном примере создается база данных appDatabase с помощью специального билдера: Room.databaseBuilder , который принимает контекст, класс, содержащий описание нашей базы данных, и название самой базы.
Так же у билдера вызван метод createFromAssets , данный метод заполняет базу данных приготовленными значениями. Т.е., если необходимо, чтобы при инициализации база данных хранила в себе какие-либо значения (например, уровни сложности и доступные результаты), нужно создать отдельно базу данных с помощью сторонних программ, таких как DB Browser for SQLite, заполнить ее и сохранить ее в папку assets приложения.
После того, как все манипуляции с базой данных были реализованы, необходимо создать data-класс Statistic , который будет использоваться во всем приложении. В данном классе создан метод toStatisticDbEntity , конвертирующий данный класс в сущность:
data class Statistic( val resultId: Long, val difficultId: Long, val mistakes: Long, val points: Long )
Теперь необходимо создать репозиторий, который будет обращаться к dao-интерфейсу и манипулировать данными базы данных:
В данном репозитории три метода, которые вставляют новые данные, получают всю статистику и удаляют какой-то элемент по идентификатору. Все эти методы являются suspend-функциями, т.к. будут вызываться из корутин. Так же следует изменить Dispatcher (withContext), т.к. обращаться к базе данных из основного потока нельзя.
Все эти методы вызываются в корутинах, запущенных во ViewModel, например вставка нового значения:
fun insertNewStatisticDataInDatabase(mistakes: Long, points: Long) < viewModelScope.launch < val newStatistic = Statistic(currentResult, currentDifficultyLevel, mistakes, points) statisticRepository.insertNewStatisticData(newStatistic.toStatisticDbEntity()) >>
Весь рабочий код Вы можете найти здесь.
Рекомендованные источники
В данной статье были разобраны основы библиотеки. Естественно, это далеко не все, что позволяет делать «Room». Если Вы хотите сильнее углубиться в эту тему изучите следующие источники, которые могут помочь Вам:
- Google Android Documentation — ссылка.
- Metanit — Введение в SQLite — ссылка.
- Android — SQLite (база данных, часть 1) — ссылка.
- Android — Room (часть 1) — ссылка.
Источник: habr.com