Room: что это, ORM библиотека и работа с SQLite

Автор: IT Sectr Опубликовано: 2026-03-12 Время чтения: 10 мин

Room — это ORM-библиотека из состава Android Jetpack, которая предоставляет слой абстракции над SQLite для работы с локальными базами данных на Android. По данным официальной документации Android Developers, 2025, Room автоматически генерирует реализации DAO на основе аннотаций во время компиляции, что устраняет около 70% шаблонного кода по сравнению с прямым использованием SQLiteOpenHelper. Библиотека выполняет проверку SQL-запросов на этапе компиляции, что позволяет выявить синтаксические ошибки до запуска приложения на устройстве.

Главное

  • Room — ORM-библиотека Android Jetpack, предоставляющая слой абстракции над SQLite для локального хранения данных в Android-приложениях.
  • Три основных компонента: Entity (описание таблицы), DAO (операции с данными) и Database (точка входа в базу).
  • Проверка SQL-запросов на этапе компиляции — ключевое преимущество, позволяющее находить ошибки до установки приложения.
  • Встроенная поддержка Flow, LiveData и RxJava для реактивного наблюдения за изменениями в базе данных.
  • Механизм миграций позволяет обновлять схему базы данных без потери уже сохранённых пользовательских данных.

Что такое Room ORM библиотека?

Room — это ORM-библиотека из состава Android Jetpack, созданная Google для упрощения работы с локальными базами данных SQLite на платформе Android. Она предоставляет аннотации для описания схемы данных и автоматически генерирует реализацию DAO-интерфейсов на этапе компиляции. В отличие от прямого использования SQLiteOpenHelper, Room избавляет разработчика от написания значительного объёма boilerplate-кода для создания, открытия и управления соединением с базой данных.

Библиотека была представлена на Google I/O 2017 как часть архитектурных компонентов Android. С тех пор Room стала стандартом де-факто для локального хранения данных, обогнав по популярности такие решения, как GreenDAO и Realm для Android. По данным Google, библиотека используется более чем в 60% приложений, опубликованных в Google Play, которые работают с локальными данными на устройстве.

Ключевая особенность — проверка SQL-запросов на этапе компиляции с помощью процессора аннотаций. Если разработчик допустил ошибку в SQL-команде, например, указал несуществующее имя столбца, сборка завершится с ошибкой до установки приложения. Это кардинально отличается от подхода SQLiteOpenHelper, где такие ошибки обнаруживаются только во время выполнения, часто в продакшне.

TypeConverters для нестандартных типов

SQLite поддерживает только пять типов данных: TEXT, INTEGER, REAL, BLOB и NULL. Однако в Java и Kotlin используются сложные типы: Date, List, Enum и кастомные объекты. Для их сохранения Room предоставляет механизм TypeConverters — статических методов, преобразующих сложный тип в примитивный, понятный SQLite. Например, объект Date конвертируется в Long (timestamp), а List<String> — в JSON-строку через Gson или Moshi.

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

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .build()

Для объявления конвертера достаточно добавить аннотацию @TypeConverter к статическому методу и указать класс конвертера в аннотации @TypeConverters на уровне базы данных. Room автоматически применяет конвертер при чтении и записи соответствующего типа в каждом SQL-запросе без ручного вызова методов преобразования.

Архитектура Room: три ключевых компонента

Room состоит из трёх основных компонентов: Entity, DAO и Database. Каждый выполняет строго определённую роль и аннотируется соответствующей аннотацией. Совместно они образуют полноценный слой доступа к данным, который изолирует бизнес-логику приложения от деталей реализации SQLite.

Entity — таблица базы данных

Entity — это класс данных, описывающий структуру одной таблицы в базе. Каждое поле класса соответствует столбцу таблицы, а каждая строка в базе — одному экземпляру класса. Аннотация @Entity указывает Room, что класс является таблицей. Поле с аннотацией @PrimaryKey задаёт первичный ключ, который может быть автоинкрементным или составным. Для связи между таблицами используется @ForeignKey, обеспечивающая целостность данных на уровне базы.

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    val age: Int,
    val email: String
)

DAO — операции с данными

DAO (Data Access Object) — это интерфейс или абстрактный класс, объявляющий операции для работы с данными: вставку, чтение, обновление и удаление. Каждая операция аннотируется @Insert, @Query, @Update или @Delete. Room автоматически генерирует реализацию этого интерфейса на этапе компиляции. Особую ценность представляет аннотация @Query, которая принимает SQL-запрос в виде строки и проверяет его корректность на этапе сборки.

kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insert(user: User): Long

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>

    @Delete
    suspend fun delete(user: User)
}

Database — точка входа

Database — это абстрактный класс, наследующий RoomDatabase, служащий точкой входа в базу данных. Он содержит список всех Entity и предоставляет абстрактные методы для получения DAO. Класс аннотируется @Database, где указываются версия схемы и список сущностей. Создание экземпляра базы выполняется через Room.databaseBuilder с указанием контекста приложения, имени файла и класса Database.

Как Room работает с SQLite под капотом

Room не заменяет SQLite, а работает поверх него как слой абстракции. Внутренняя архитектура включает процессор аннотаций, генератор кода и пул соединений. На этапе компиляции процессор аннотаций анализирует классы Entity, DAO и Database, после чего генерирует классы-реализации с суффиксом _Impl. Все сгенерированные классы помещаются в пакет сборки и не видны разработчику напрямую.

Генерация кода на этапе компиляции — центральный механизм Room. Для каждого DAO-интерфейса генерируется класс с полной реализацией всех аннотированных методов. SQL-запросы из аннотации @Query проверяются на корректность: процессор сопоставляет имена столбцов с полями Entity и проверяет синтаксис SQL. При обнаружении ошибки сборка прерывается с понятным сообщением. Это невозможно при использовании сырого SQLiteOpenHelper, где ошибки проявляются только в runtime.

Генерация кода на этапе компиляции

Процесс генерации включает три этапа. Первый — валидация схемы: процессор проверяет, что все классы, перечисленные в @Database, являются корректными Entity. Второй — генерация тела DAO: для каждого метода создаётся реализация с использованием внутреннего объекта RoomSQLiteQuery, выполняющего prepared-запросы. Третий — генерация класса Database_Impl, реализующего создание и открытие базы, а также инициализацию всех DAO-объектов.

kotlin
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
    private val __insertionAdapter = __db
        .createInsertionAdapter(User::class, 0)

    override suspend fun insert(user: User): Long {
        __db.assertNotSuspendingTransaction()
        return __db.runInTransaction {
            __insertionAdapter.insertAndReturnId(user)
        }
    }
}

Room не создаёт отдельный пул потоков для операций с базой. По умолчанию запросы выполняются в вызывающем потоке с одним ограничением: чтение и запись блокируют поток. Для асинхронной работы Room интегрируется с корутинами Kotlin через suspend-функции, с LiveData через возвращаемые значения и с Flow через реактивные обёртки. Это даёт разработчику гибкость выбора архитектурного решения под конкретную задачу.

Пример использования Room в Android приложении

Рассмотрим практический пример создания приложения для хранения заметок с использованием Room. Приложение содержит одну таблицу Note с полями id, title, content и timestamp. Пользователь сможет добавлять, просматривать и удалять заметки. Для демонстрации применяются корутины для асинхронных операций.

Настройка зависимостей Gradle

Для подключения Room в проект Android необходимо добавить зависимости в файл build.gradle модуля приложения. Room требует три компонента: runtime-библиотеку, процессор аннотаций kapt и опциональную поддержку корутин. Версия библиотеки указывается в переменной room_version для удобства обновления. Начиная с Room 2.4.0, поддерживается KSP как альтернатива kapt с более высокой скоростью сборки.

groovy
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    // Опционально: тестирование
    testImplementation "androidx.room:room-testing:$room_version"
}

После настройки зависимостей создаются три файла: Note Entity, NoteDao интерфейс и AppDatabase класс. Entity Note содержит поля с аннотациями @PrimaryKey и @ColumnInfo. DAO предоставляет методы для вставки, получения списка и удаления. Database связывает Entity и DAO через аннотацию @Database.

kotlin
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String,
    @ColumnInfo(name = "created_at")
    val timestamp: Long = System.currentTimeMillis()
)

@Dao
interface NoteDao {
    @Insert
    suspend fun insert(note: Note)

    @Query("SELECT * FROM notes ORDER BY created_at DESC")
    fun getAllNotes(): Flow<List<Note>>

    @Delete
    suspend fun delete(note: Note)
}

Файл AppDatabase объявляется как абстрактный класс, наследующий RoomDatabase. В аннотации @Database указываются все Entity текущей версии и номер версии схемы. Для получения экземпляра используется паттерн синглтон через build-метод Room.databaseBuilder с контекстом приложения. Кэширование экземпляра базы предотвращает множественные создания, которые могут привести к утечкам памяти.

Миграции базы данных в Room

Миграции в Room — это механизм для изменения схемы базы данных при обновлении приложения без потери существующих данных. Когда пользователь устанавливает новую версию с изменёнными Entity, Room обнаруживает несоответствие версий и выполняет указанные миграционные шаги. Без миграции база данных будет удалена и создана заново, что приведёт к потере всех сохранённых пользователем данных.

Миграция описывается классом Migration, принимающим начальную и конечную версию базы. Внутри метода migrate выполняется SQL-запрос ALTER TABLE или CREATE TABLE для изменения схемы. Room не умеет автоматически определять изменения схемы — разработчик обязан написать миграцию вручную для каждого изменения Entity. Начиная с версии Room 2.4.0, доступна экспериментальная функция autoMigrations для автоматической генерации миграций.

Автоматические миграции с autoMigrations

Функция autoMigrations позволяет Room автоматически генерировать миграции на основе различий между версиями Entity. Для её использования достаточно добавить аннотацию @AutoMigration в @Database и указать экспорт схемы в JSON. Room сравнивает схемы соседних версий и генерирует необходимые ALTER-запросы. Однако autoMigrations поддерживает только обратно совместимые изменения: добавление колонок, создание индексов и изменение типов с совместимыми преобразованиями.

kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL(
            "ALTER TABLE users ADD COLUMN phone TEXT"
        )
    }
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .addMigrations(MIGRATION_1_2)
    .build()

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

Часто задаваемые вопросы

Чем Room отличается от SQLiteOpenHelper?

Room предоставляет ORM-абстракцию с аннотациями и проверкой SQL на этапе компиляции, тогда как SQLiteOpenHelper требует ручного написания всех запросов и управления соединением. Room автоматически генерирует код для CRUD-операций и интегрируется с архитектурными компонентами Android, включая LiveData и Flow.

Какие типы данных поддерживает Room?

Room поддерживает все примитивные типы Java: Int, Long, Boolean, Float, Double, а также String, ByteArray и Date. Для сложных типов, таких как List или Enum, используются TypeConverters — статические методы преобразования, которые конвертируют нестандартные типы в поддерживаемые SQLite форматы.

Можно ли использовать Room без корутин?

Да, Room поддерживает синхронные вызовы без корутин, но они блокируют поток, в котором выполняются. Для асинхронной работы можно использовать LiveData или RxJava вместо корутин. Google рекомендует применять корутины как основной способ асинхронного доступа к данным в новых проектах.

Что произойдёт при отсутствии миграции?

Если Room обнаруживает несоответствие версии базы данных и не находит подходящей миграции, по умолчанию возникает IllegalStateException с описанием ошибки. Разработчик может переопределить это поведение методом fallbackToDestructiveMigration, который удалит существующую базу и создаст новую с потерей всех данных.

Как Room обрабатывает связи между таблицами?

Room поддерживает связи через вложенные объекты с аннотацией @Embedded и через классы отношений с аннотацией @Relation. Для сложных запросов с объединением таблиц используются кастомные POJO-классы, поля которых заполняются из результатов @Query с SQL-оператором JOIN.

Итоги

  • Room — ORM-библиотека Android Jetpack, создающая слой абстракции над SQLite для удобного хранения данных на устройстве.
  • Архитектура основана на трёх компонентах: Entity (схема таблицы), DAO (операции) и Database (точка входа).
  • Проверка SQL-запросов на этапе компиляции — главное преимущество, исключающее runtime-ошибки в запросах.
  • Встроенная поддержка Flow, LiveData и RxJava позволяет строить реактивные архитектуры с автоматическим обновлением UI при изменении данных.
  • Миграции в Room обеспечивают бесшовное обновление схемы базы данных без потери сохранённой информации пользователя.
  • Библиотека интегрируется с корутинами Kotlin через suspend-функции, упрощая асинхронную работу с данными.
  • Для новых проектов Room является официально рекомендуемым решением Google для локального хранения данных на Android.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также