Room

https://developer.android.com/training/data-storage/room
https://developer.android.com/training/data-storage/room/defining-data
https://developer.android.com/training/data-storage/room/accessing-data
https://developer.android.com/training/data-storage/room/relationships
https://developer.android.com/training/data-storage/room/async-queries
https://developer.android.com/training/data-storage/room/creating-views
https://developer.android.com/training/data-storage/room/prepopulate
https://developer.android.com/training/data-storage/room/migrating-db-versions
https://developer.android.com/training/data-storage/room/testing-db
https://developer.android.com/training/data-storage/room/referencing-data
https://developer.android.com/training/data-storage/room/sqlite-room-migration
https://developer.android.com/training/data-storage/room/room-kmp-migration
https://developer.android.com/kotlin/multiplatform/sqlite
Room

Абстракция над SQLite для надежного доступа к базе данных.

Получить количество записей в таблице

@Query("SELECT COUNT(*) FROM users")
fun usersCount(): Flow<Int>

Проверить существование записи в таблице

@Query("SELECT EXISTS(SELECT * FROM users WHERE userId = :userId)")
suspend fun isExist(userId: Int): Boolean

Фильтр по диапазону значений

@Query("SELECT * FROM users WHERE type IN (:ids)")
fun getUsers(ids: List<Int>): Flow<List<UserDb>>

Обновление значения

Если Flow не триггерится после обновления значения с использованием Relation.

@Update
suspend fun update(userDb: UserDb)
@Database

Маркирует класс-наследник RoomDatabase для конфигурирования базы данных. Должен быть абстрактным.

@Database(entities = [User::class], version = 1)
abstract class AppDatabase: RoomDatabase() {
    abstract fun userDao(): UserDao
}
@Entity

Сущность данных. Объект данных в Room. Каждый объект - отдельная таблица в базе данных. Каждый экземпляр объекта - строка в таблице.

@Entity
data class User(
    @PrimaryKey val userId: Int,
    val name: String,
    @ColumnInfo(name = "user_age") val age: Int
)
@Dao

Data access object. Предоставляет методы для взаимодействия с данными в таблице.

@Dao
interface UserDao {
    
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}
@ForeignKey

В Room внешние ключи (ForeignKey) используются для создания связей между таблицами, обеспечивая целостность данных. При объявлении внешнего ключа в сущности (Entity) указываются поля, которые будут ссылаться на другие таблицы. Это позволяет реализовать отношения, такие как один ко многим.

@Entity
data class Order(
    @PrimaryKey val orderId: Long,
    @ForeignKey(entity = Customer::class, parentColumns = ["customerId"], childColumns = ["customerId"]) val customerId: Long
)

@Entity
data class Customer(
    @PrimaryKey val customerId: Long,
    val name: String
)
@RewriteQueriesToDropUnusedColumns

Используется с аннотацией Query и позволяет извлечь из таблицы только те столбцы которые указаны в модели для ответа.

data class NameAndLastName (
    val name: String,
    val lastName: String
)

@Dao
interface MyDao {
    
	  @Query("SELECT * FROM User")
    public fun getAll(): List<NameAndLastName> // извлечет все поля, не только name и lastName
}

@Dao
interface MyDao {
    
    @RewriteQueriesToDropUnusedColumns
    @Query("SELECT * FROM User")
    fun getAll(): List<NameAndLastName> // извлечет только name и lastName
}
Вопросы на собесе (16)
  1. Что такое Room?

    Библиотека для работы с базами данных в Android, которая предоставляет абстракцию над SQLite.

  1. Преимущества Room по сравнению с SQLite?

    Типобезопасность — Room проверяет запросы на этапе компиляции.

    Упрощенное взаимодействие — благодаря DAO можно использовать аннотации для определения запросов без необходимости писать SQL.

    Автоматическая миграция — упрощает обновление схемы базы данных.

    Поддержка LiveData и Coroutines — позволяет работать с асинхронностью и реактивным программированием.

    Упрощение обработки отношений — поддержка «‎один ко многим»‎ и «‎многие ко многим»‎.

  1. Какие основные компоненты Room?

    @Entity - класс, представляющий таблицу в базе данных.

    @Dao (Data Access Object) - интерфейс для определения методов доступа к данным.

    @Database - абстрактный класс, который объединяет все сущности и DAO, а также управляет базой данных.

  1. Как работают аннотации @Query @Insert @Update @Delete в Room?

    Используются для определения SQL-запросов к базе данных.

  1. Что произойдет, если не настроить миграцию при изменении схемы базы данных?

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

  1. Как работать с отношениями между сущностями в Room (например, «‎один ко многим» или «‎многие ко многим»)?

    Один ко многим: создайте две сущности. В основной сущности добавьте поле для идентификатора связанной сущности. Используйте аннотацию @Relation для создания списка связанных объектов в основной сущности.

    Многие ко многим: создайте третью сущность, которая будет представлять связь между двумя сущностями (состоящую из двух полей идентификаторов). Затем используйте аннотацию @Relation в обеих сущностях для определения связи через эту промежуточную таблицу.

  1. Как использовать типы данных, не поддерживаемые напрямую Room, такие как Date или List?

    Использовать @TypeConverter.

  1. Как Room обрабатывает конфликты при вставке или обновлении данных?

    Room обрабатывает конфликты при вставке или обновлении данных с помощью стратегии разрешения конфликтов. По умолчанию используется стратегия IGNORE, но можно указать другие, такие как REPLACE или ABORT. Это позволяет управлять тем, как Room будет реагировать на конфликты, например, игнорировать конфликтующие записи или заменять их новыми данными.

  1. Как объединить несколько таблиц в Room?

    Использовать аннотацию @Relation в Entity-классах для отображения отношений «‎один ко многим» или «‎многие ко многим», либо использовать запросы с Join в DAO для выполнения более сложных операций с несколькими таблицами.

  1. Как работает каскадное удаление в Room?

    Каскадное удаление в Room позволяет автоматически удалять связанные записи в базе данных при удалении основной записи. Это реализуется с помощью аннотации @ForeignKey с параметром onDelete = ForeignKey.CASCADE в определении связи между сущностями.

  1. Какие есть базы данных кроме Room?

    SQLite

    Realm

    SQLDelight

  1. Чем Room отличается от Realm?

    Room — это библиотека для работы с SQLite и реляционными данными, интегрированная с архитектурой Android. Realm — собственная база данных, работающая с объектами напрямую (NoSQL), предлагающая высокую производительность, но с ограничениями по миграции и сложным запросам.

  1. Как связать 2 Entity между собой?

    Использовать аннотацию @Relation для создания связи между ними. Например, можно создать один класс для родительской сущности и другой для дочерней.

  1. Как файл базы данных Room хранится в Android?

    Файл базы данных Room хранится в каталоге приложения на устройстве Android, обычно по пути /data/data/your.package.name/databases/your_database_name.db. Этот файл доступен только приложению, которое его создало, и содержит все таблицы, индексы и данные, которые вы определили в своих Entity-классах. Room автоматически управляет созданием и обновлением базы данных при изменении схемы.

  1. Почему файл базы данных Room не может прочитать другое приложение?

    Из-за механизма безопасности Android, который изолирует данные приложений. Каждое приложение работает в своем собственном контейнере, и доступ к файловой системе ограничен. По умолчанию файлы в каталоге /data/data/your.package.name/ защищены, и другие приложения не имеют разрешения на их чтение или запись, что предотвращает несанкционированный доступ к данным.

  1. Какие виды связей существуют в базах данных?

    Один к одному (1:1) — связь между двумя таблицами, где каждой записи в одной таблице соответствует одна запись в другой.

    Один ко многим (1:N) — связь, где одной записи в таблице А соответствуют несколько записей в таблице B.

    Многие к одному (N:1) — несколько записей из одной таблицы могут ссылаться на одну запись в другой.

    Многие ко многим (M:N) — связь, в которой записи из одной таблицы могут соответствовать множеству записей в другой, и наоборот.