Room

Room - обертка над SQLIte/Realm/DAO, представленная на Goolge I/O. Room provides an abstraction layer over SQLite in a similar way to Retrofit with network requests. Пример использования: androidx.room. https://habr.com/ru/post/336196/arrow-up-right https://developer.android.com/training/data-storage/room/accessing-dataarrow-up-right

Выглядит как достаточно безопасная штука, которая переводит объекты сразу в базу и обратно. Если делать все по инструкции, ошибиться невозможно (про sqli) Подробно: https://medium.com/@appmattus/android-security-sql-injection-with-the-room-persistence-library-69f4e286960farrow-up-right

Две норм статьи по разработке с помощью Room: https://medium.com/androiddevelopers/7-steps-to-room-27a5fe5f99b2arrow-up-right https://medium.com/androiddevelopers/7-pro-tips-for-room-fbadea4bfbd1arrow-up-right

С точки зрения разработки

Поэтапное создание https://codelabs.developers.google.com/codelabs/android-room-with-a-view-kotlin/#4arrow-up-right

Подключение в проект

В build.gradle файл проекта добавьте репозитарий google()

allprojects {
    repositories {
        jcenter()
        google()
    }
    ...
}

В build.gradle файле модуля добавьте dependencies:

dependencies {
    implementation "android.arch.persistence.room:runtime:1.0.0"
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
    ...
}

Структура

Три основных компонента - Entity, Dao, Database Entity - объект, который хотим хранить в базе. Используется для создания таблицы. Database - Dao - описывает методы для работы с базой данных.

Entity

По умолчанию, имя создаваемой таблицы будет название класса. Следующим образом задается свое название таблицы:

Переопределить имя поля:

По умолчанию Room определяет тип данных для поля в таблице по типу данных поля в Entity классе. Но мы можем явно указать свой тип.

У PrimaryKey есть параметр autoGenerate. Он позволяет включить для поля режим autoincrement, в котором база данных сама будет генерировать значение, если вы его не укажете.

Чтобы создать составной ключ, используйте параметр primaryKeys.

Внешний ключ (ForeignKeys) Внешние ключи позволяют связывать таблицы между собой. Если вы еще не знакомы с ними, то можете почитать о них в инете. В выше рассмотренных примерах у нас есть класс Employee для хранения данных по сотрудникам. Давайте создадим класс Car для хранения данных по машинам. И каждая машина должна быть прикреплена к какому-либо сотруднику.

Важно: When we use foreign key dont forget to put onDelete = ForeignKey.CASCADE this way if you delete an data it will also delete de dependency. This way you won't have false data or data that are never use

Индекс (Index) Индексы могут повысить производительность вашей таблицы. Если вы еще не знакомы с ними, то можете почитать о них в инете. В аннотации Entity есть параметр indicies, который позволяет задавать индексы.

Создаем два индекса: один по полю salary, а другой по двум полям first_name и last_name.

Индекс для одного поля также может быть настроен через параметр index аннотации ColumnInfo

Будет создан индекс для поле salary.

Вложенные объекты Пусть у нас есть класс Address, с данными о адресе. Это обычный класс, не Entity.

И мы хотим использовать его в Entity классе Employee

Простое решение - использовать аннотацию Embedded. Если у вас получается так, что совпадают имена каких-то полей в основном объекте и в Embedded объекте, то используйте префикс для Embedded объекта.

В этом случае к именам полей Embedded объекта в таблице будет добавлен указанный префикс.

Ignore Аннотация Ignore позволяет подсказать Room, что это поле не должно записываться в базу или читаться из нее.

Dao

Обратите внимание, что в качестве имени таблицы мы используем employee. Напомню, что имя таблицы равно имени Entity класса, т.е. Employee, но в SQLite не важен регистр в именах таблиц, поэтому можем писать employee. Для вставки/обновления/удаления используются методы insert/update/delete с соответствующими аннотациями. Тут никакие запросы указывать не нужно. Названия методов могут быть любыми. Главное - аннотации.

Database

Аннотацией Database помечаем основной класс по работе с базой данных. Этот класс должен быть абстрактным и наследовать RoomDatabase.

В параметрах аннотации Database указываем, какие Entity будут использоваться, и версию базы. Для каждого Entity класса из списка entities будет создана таблица.

В Database классе необходимо описать абстрактные методы для получения Dao объектов, которые вам понадобятся.

Как пользоваться

Database объект - это стартовая точка. Его создание выглядит так:

Используем Application Context, а также указываем AppDatabase класс и имя файла для базы.

Учитывайте, что при вызове этого кода Room каждый раз будет создавать новый экземпляр AppDatabase. Эти экземпляры очень тяжелые и рекомендуется использовать один экземпляр для всех ваших операций. Поэтому вам необходимо позаботиться о синглтоне для этого объекта. Это можно сделать с помощью Dagger, например.

Если вы не используете Dagger (или другой DI механизм), то можно использовать Application класс для создания и хранения AppDatabase:

Не забудьте добавить App класс в манифест

В коде получение базы будет выглядеть так:

Из Database объекта получаем Dao.

Теперь мы можем работать с Employee объектами. Но эти операции должны выполняться не в UI потоке. Иначе мы получим Exception.

Добавление нового сотрудника в базу будет выглядеть так:

Метод getAll вернет нам всех сотрудников в List

Получение сотрудника по id:

UI поток

Операции по работе с базой данных - синхронные, и должны выполняться не в UI потоке.

В случае с Query операциями мы можем сделать их асинхронными используя LiveData или RxJava.

Last updated