Room — это библиотека для работы с SQLite в Android, входящая в состав Jetpack. Она предоставляет слой абстракции над сырым SQLite, автоматизируя создание таблиц, выполнение запросов и конвертацию данных в объекты Kotlin и Java. По данным Android Developers, Room компилирует SQL-запросы на этапе сборки, проверяя корректность синтаксиса и связей между Entity и таблицами.
Главное
Room — это библиотека персистентности из состава Android Jetpack, предоставляющая объектно-реляционное отображение для SQLite. Room решает три основные проблемы сырого SQLite: написание большого объёма boilerplate-кода для создания таблиц, отсутствие проверки SQL-запросов на этапе компиляции и ручная конвертация Cursor в объекты.
Библиотека использует компилятор аннотаций (kapt или KSP), который генерирует реализацию абстрактных классов RoomDatabase и DAO на этапе сборки. Это гарантирует, что синтаксические ошибки в SQL и несоответствия типов обнаруживаются до запуска приложения, а не в runtime после публикации в Google Play.
По данным Google I/O 2023, Room используется в 68% Android-приложений, которые работают с локальными данными. Это стандарт хранения данных на устройстве, рекомендованный Google для всех новых проектов — вместо устаревших SQLiteOpenHelper и ContentProvider.
Внедряйте Room в проекты, где требуется локальное кэширование данных с сервера, офлайн-режим или хранение структурированных пользовательских данных с возможностью сложных SQL-запросов.
Room является частью Android Jetpack и официально рекомендован Google для всех новых проектов, работающих с локальными данными. В отличие от Realm или ObjectBox, Room использует нативный SQLite, что гарантирует совместимость с любыми сторонними инструментами для работы с базой — от DB Browser до DataGrip. Разработчик может открыть .db-файл приложения и выполнять SQL-запросы напрямую, что упрощает отладку и анализ данных в процессе разработки.
Entity — это класс данных, аннотированный @Entity, который Room преобразует в таблицу базы данных. Каждое поле класса становится столбцом таблицы, а каждый экземпляр — строкой. Room использует рефлексию для доступа к полям, поэтому требуется аннотация @PrimaryKey для обязательного идентификатора.
Аннотация @Entity указывает Room, что класс является таблицей. Параметр tableName задаёт имя таблицы, если оно отличается от имени класса. @PrimaryKey определяет первичный ключ с возможностью авто-генерации через autoGenerate = true.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "full_name")
val name: String,
@Ignore
val tempData: String?
)
@ColumnInfo задаёт имя столбца в таблице, если оно отличается от имени поля Kotlin. @Ignore исключает поле из таблицы — оно не будет сохранено в базу. @ForeignKey описывает внешние ключи для связей между таблицами с каскадными операциями при удалении или обновлении.
Room поддерживает вложенные объекты через аннотацию @Embedded. Поля вложенного класса разворачиваются в столбцы родительской таблицы с префиксом для избежания конфликта имён. Например, класс Address с полями city и street, встроенный в User, создаст столбцы address_city и address_street в таблице users, избавляя от создания отдельных таблиц для простых value-объектов.
Room поддерживает только примитивные типы и их обёртки. Для хранения списков, Date или кастомных типов используется @TypeConverter — статические методы конвертации между кастомным типом и примитивом SQLite, например, между List и строкой JSON.
DAO (Data Access Object) — это интерфейс или абстрактный класс, аннотированный @Dao, содержащий методы для доступа к данным. Каждый метод аннотируется SQL-операцией: @Insert, @Update, @Delete или @Query с явным SQL-запросом.
Аннотация @Query принимает строку SQL, которая проверяется Room на этапе компиляции на корректность синтаксиса и соответствие имён столбцов полям Entity. Room поддерживает параметризованные запросы через синтаксис :paramName.
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("SELECT * FROM users ORDER BY name ASC")
fun getAllUsers(): Flow<List<User>>
}
@Insert поддерживает стратегии OnConflictStrategy для обработки конфликтов при вставке дублирующихся записей. Flow как возвращаемый тип обеспечивает реактивное обновление UI при каждом изменении данных в таблице — подписка автоматически перезапускается при любом INSERT, UPDATE или DELETE.
Аннотация @Transaction гарантирует атомарное выполнение нескольких операций в одном транзакционном блоке. Room блокирует базу на время выполнения, предотвращая состояние гонки при конкурентном доступе из нескольких потоков.
RoomDatabase — абстрактный класс, который объединяет Entity и DAO в единую точку доступа к базе. Он создаётся через Room.databaseBuilder с указанием версии схемы и списка Entity-классов. Экземпляр базы рекомендуется создавать как синглтон через lazy-делегат для избежания множественных подключений.
Миграция в Room — это класс Migration, описывающий SQL-скрипт для перехода от старой версии схемы к новой. Если миграция не предоставлена при изменении схемы, Room падает с IllegalStateException. Это защищает от случайной потери данных пользователя при обновлении приложения.
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
}
}
val db = Room.databaseBuilder(
getApplication(),
AppDatabase::class.java,
"app_database"
).addMigrations(migration_1_2)
.build()
Для разработки можно использовать fallbackToDestructiveMigration, который удаляет старую базу и создаёт новую при несовпадении версий. Этот режим предназначен только для отладки — в продакшн-релизах обязательно пишутся миграции.
Для тестирования базы данных Room предоставляет специальный класс Room.inMemoryTestBuilder, который создаёт базу в оперативной памяти без сохранения на диск. После завершения каждого теста база автоматически уничтожается, что гарантирует полную изоляцию тестовых сценариев. В сочетании с библиотекой android-arch-core-testing разработчик может управлять жизненным циклом базы и проверять корректность миграций без необходимости чистить состояние вручную.
Производительность Room напрямую зависит от структуры запросов и индексов. Для анализа медленных запросов Room предоставляет флаг enableQueryCallback, который логирует все SQL-запросы с временем выполнения. Разработчик может использовать этот лог для поиска запросов, выполняющихся дольше 100 миллисекунд, и оптимизировать их добавлением составных индексов через аннотацию @Index в @Entity или переписыванием подзапросов на прямые JOIN-соединения с использованием @Relation.
Room также поддерживает шифрование базы данных через SQLCipher. Подключение библиотеки net.zetetic:android-database-sqlcipher и использование SupportFactory вместо стандартного обеспечивает прозрачное шифрование всех данных на диске без изменения DAO-запросов и структуры Entity. Это необходимо для приложений, работающих с персональными данными пользователей, и соответствует требованиям GDPR и российского 152-ФЗ о защите персональных данных. Пароль шифрования может храниться в Android Keystore для защиты от извлечения через инструментарий на рутованных устройствах.
Room нативно поддерживает Kotlin Coroutines начиная с версии 2.1. DAO-методы могут быть suspend-функциями, выполняющими запросы в фоновом потоке без блокировки главного. Room автоматически управляет диспетчерами, используя Dispatchers.IO для запросов чтения и записи.
Для реактивных запросов Room возвращает Flow — холодный поток данных, который эмитит новое значение при каждом изменении затронутой таблицы. ViewModel подписывается на Flow через stateIn или collect, обеспечивая автоматическое обновление UI без ручного уведомления адаптера.
Room поддерживает также Paging 3 через специальную реализацию PagingSource, которая загружает данные постранично из SQLite. Это эффективно для больших списков с тысячами записей: Paging 3 загружает только видимые на экране строки и автоматически обновляет их при изменениях в базе.
Используйте Paging 3 с Room при отображении ленты новостей, лога операций или списка товаров с возможностью офлайн-доступа и бесконечной прокруткой.
Часто задаваемые вопросы
Room автоматизирует создание таблиц, конвертацию Cursor в объекты и проверку SQL на этапе компиляции. SQLiteOpenHelper требует писать схему вручную, обрабатывать Cursor и не имеет проверки запросов до запуска приложения, что увеличивает риск ошибок.
Да, при изменении Entity (добавление/удаление поля, изменение типа) требуется миграция. Без неё Room выбрасывает IllegalStateException при запуске. Для разработки можно включить fallbackToDestructiveMigration, но в релизе обязательны корректные скрипты миграции.
Room поддерживает @ForeignKey для каскадных операций и @Relation для вложенных объектов. Для сложных JOIN-запросов используется аннотация @Transaction с @Query, возвращающим POJO с вложенными сущностями через @Embedded и @Relation.
Да, Room полностью совместим с Java. Вместо suspend-функций используются LiveData или RxJava Observable, вместо Flow — LiveData. Room с Java поддерживает все те же аннотации, но требует больше boilerplate-кода для асинхронных операций.
Room поддерживает шифрование через SQLCipher от Zetetic. Вместо Room.databaseBuilder используйте SupportFactory из библиотеки net.zetetic:android-database-sqlcipher, передав пароль шифрования. Все данные на диске будут зашифрованы прозрачно для DAO-запросов.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также