Room: ce este, biblioteca ORM și lucrul cu SQLite

Autor: IT Sectr Publicat: 2026-03-12 Timp de citire: 10 min

Room este o bibliotecă ORM din cadrul Android Jetpack care oferă un strat de abstractizare peste SQLite pentru lucrul cu baze de date locale pe Android. Conform documentației oficiale Android Developers, 2025, Room generează automat implementări DAO pe baza adnotărilor în timpul compilării, eliminând aproximativ 70% din codul boilerplate față de utilizarea directă a SQLiteOpenHelper. Biblioteca efectuează verificarea interogărilor SQL în faza de compilare, permițând depistarea erorilor sintactice înainte de lansarea aplicației pe dispozitiv.

Principalele puncte

  • Room — bibliotecă ORM Android Jetpack care oferă un strat de abstractizare peste SQLite pentru stocarea locală a datelor în aplicațiile Android.
  • Trei componente principale: Entity (descrierea tabelului), DAO (operații cu date) și Database (punct de intrare în bază).
  • Verificarea interogărilor SQL în faza de compilare — avantajul cheie care permite găsirea erorilor înainte de instalarea aplicației.
  • Suport încorporat pentru Flow, LiveData și RxJava pentru observarea reactivă a modificărilor din baza de date.
  • Mecanismul de migrări permite actualizarea schemei bazei de date fără pierderea datelor deja salvate ale utilizatorului.

Ce este biblioteca ORM Room?

Room este o bibliotecă ORM din cadrul Android Jetpack, creată de Google pentru simplificarea lucrului cu bazele de date locale SQLite pe platforma Android. Oferă adnotări pentru descrierea schemei datelor și generează automat implementarea interfețelor DAO în faza de compilare. Spre deosebire de utilizarea directă a SQLiteOpenHelper, Room eliberează dezvoltatorul de scrierea unei cantități semnificative de cod boilerplate pentru crearea, deschiderea și gestionarea conexiunii cu baza de date.

Biblioteca a fost prezentată la Google I/O 2017 ca parte a componentelor arhitecturale Android. De atunci Room a devenit standardul de facto pentru stocarea locală a datelor, depășind ca popularitate soluții precum GreenDAO și Realm pentru Android. Potrivit Google, biblioteca este utilizată în peste 60% din aplicațiile publicate în Google Play care lucrează cu date locale pe dispozitiv.

Caracteristica cheie — verificarea interogărilor SQL în faza de compilare cu ajutorul procesorului de adnotări. Dacă dezvoltatorul greșește într-o comandă SQL, de exemplu, indică un nume de coloană inexistent, compilarea se va încheia cu eroare înainte de instalarea aplicației. Aceasta diferă radical de abordarea SQLiteOpenHelper, unde astfel de erori sunt descoperite doar în timpul execuției, adesea în producție.

TypeConverters pentru tipuri nestandard

SQLite suportă doar cinci tipuri de date: TEXT, INTEGER, REAL, BLOB și NULL. Însă în Java și Kotlin se folosesc tipuri complexe: Date, List, Enum și obiecte personalizate. Pentru salvarea lor, Room oferă mecanismul TypeConverters — metode statice care convertesc un tip complex într-un tip primitiv, inteligibil pentru SQLite. De exemplu, obiectul Date este convertit în Long (timestamp), iar List<String> — în șir JSON prin Gson sau 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()

Pentru a declara convertizorul, este suficient să adăugați adnotarea @TypeConverter la o metodă statică și să indicați clasa convertizorului în adnotarea @TypeConverters la nivelul bazei de date. Room aplică automat convertizorul la citirea și scrierea tipului corespunzător în fiecare interogare SQL, fără apelarea manuală a metodelor de conversie.

Arhitectura Room: trei componente cheie

Room este format din trei componente principale: Entity, DAO și Database. Fiecare îndeplinește un rol strict definit și este adnotat cu adnotarea corespunzătoare. Împreună, ele formează un strat complet de acces la date care izolează logica de afaceri a aplicației de detaliile de implementare SQLite.

Entity — tabela bazei de date

Entity este o clasă de date care descrie structura unui tabel în bază. Fiecare câmp al clasei corespunde unei coloane a tabelului, iar fiecare rând în bază — unei instanțe a clasei. Adnotarea @Entity indică Room că clasa este un tabel. Câmpul cu adnotarea @PrimaryKey definește cheia primară, care poate fi auto-incrementată sau compusă. Pentru legătura între tabele se folosește @ForeignKey, asigurând integritatea datelor la nivelul bazei.

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 — operații cu date

DAO (Data Access Object) este o interfață sau clasă abstractă care declară operațiile pentru lucrul cu datele: inserare, citire, actualizare și ștergere. Fiecare operație este adnotată cu @Insert, @Query, @Update sau @Delete. Room generează automat implementarea acestei interfețe în faza de compilare. O valoare deosebită o are adnotarea @Query, care primește o interogare SQL sub formă de șir și verifică corectitudinea acesteia în faza de construire.

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 — punctul de intrare

Database este o clasă abstractă care moștenește RoomDatabase, servind ca punct de intrare în baza de date. Conține lista tuturor Entity și oferă metode abstracte pentru obținerea DAO. Clasa este adnotată cu @Database, unde se specifică versiunea schemei și lista entităților. Crearea instanței bazei se realizează prin Room.databaseBuilder cu specificarea contextului aplicației, numelui fișierului și clasei Database.

Cum funcționează Room cu SQLite sub capotă

Room nu înlocuiește SQLite, ci funcționează deasupra lui ca un strat de abstractizare. Arhitectura internă include procesorul de adnotări, generatorul de cod și un pool de conexiuni. În faza de compilare, procesorul de adnotări analizează clasele Entity, DAO și Database, după care generează clase de implementare cu sufixul _Impl. Toate clasele generate sunt plasate în pachetul de compilare și nu sunt vizibile direct dezvoltatorului.

Generarea codului în faza de compilare — mecanismul central al Room. Pentru fiecare interfață DAO se generează o clasă cu implementarea completă a tuturor metodelor adnotate. Interogările SQL din adnotarea @Query sunt verificate pentru corectitudine: procesorul potrivește numele coloanelor cu câmpurile Entity și verifică sintaxa SQL. La detectarea unei erori, compilarea este întreruptă cu un mesaj clar. Acest lucru este imposibil la utilizarea SQLiteOpenHelper direct, unde erorile apar doar în runtime.

Generarea codului în faza de compilare

Procesul de generare include trei etape. Prima — validarea schemei: procesorul verifică dacă toate clasele enumerate în @Database sunt Entity corecte. A doua — generarea corpului DAO: pentru fiecare metodă se creează o implementare folosind obiectul intern RoomSQLiteQuery, care execută interogări pregătite. A treia — generarea clasei Database_Impl, care implementează crearea și deschiderea bazei, precum și inițializarea tuturor obiectelor 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 nu creează un pool separat de fire de execuție pentru operațiile cu baza. În mod implicit, interogările sunt executate în firul de apelare cu o singură restricție: citirea și scrierea blochează firul. Pentru lucrul asincron, Room se integrează cu corutinele Kotlin prin funcții suspend, cu LiveData prin valori returnate și cu Flow prin învelitori reactive. Aceasta oferă dezvoltatorului flexibilitate în alegerea soluției arhitecturale pentru o sarcină concretă.

Exemplu de utilizare Room în aplicația Android

Să analizăm un exemplu practic de creare a unei aplicații pentru stocarea notițelor folosind Room. Aplicația conține un tabel Note cu câmpurile id, title, content și timestamp. Utilizatorul va putea adăuga, vizualiza și șterge notițe. Pentru demonstrare se folosesc corutine pentru operații asincrone.

Configurarea dependențelor Gradle

Pentru a conecta Room la un proiect Android, trebuie adăugate dependențe în fișierul build.gradle al modulului aplicației. Room necesită trei componente: biblioteca runtime, procesorul de adnotări kapt și suportul opțional pentru corutine. Versiunea bibliotecii este specificată în variabila room_version pentru comoditatea actualizării. Începând cu Room 2.4.0, KSP este suportat ca alternativă la kapt cu o viteză de compilare mai mare.

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"
    // Opțional: testare
    testImplementation "androidx.room:room-testing:$room_version"
}

După configurarea dependențelor, se creează trei fișiere: Entity Note, interfața NoteDao și clasa AppDatabase. Entity Note conține câmpuri cu adnotările @PrimaryKey și @ColumnInfo. DAO oferă metode pentru inserare, obținerea listei și ștergere. Database conectează Entity și DAO prin adnotarea @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)
}

Fișierul AppDatabase este declarat ca o clasă abstractă care moștenește RoomDatabase. În adnotarea @Database sunt enumerate toate Entity ale versiunii curente și numărul versiunii schemei. Pentru obținerea instanței se folosește modelul singleton prin metoda build a Room.databaseBuilder cu contextul aplicației. Stocarea în cache a instanței bazei previne creările multiple care ar putea duce la scurgeri de memorie.

Migrări ale bazei de date în Room

Migrările în Room sunt un mecanism pentru modificarea schemei bazei de date la actualizarea aplicației fără pierderea datelor existente. Când utilizatorul instalează o nouă versiune cu Entity modificate, Room detectează nepotrivirea versiunilor și execută pașii de migrare specificați. Fără migrare, baza de date va fi ștearsă și recreată, ceea ce va duce la pierderea tuturor datelor salvate de utilizator.

Migrarea este descrisă de clasa Migration, care primește versiunea inițială și finală a bazei. În interiorul metodei migrate se execută o interogare SQL ALTER TABLE sau CREATE TABLE pentru modificarea schemei. Room nu poate determina automat modificările schemei — dezvoltatorul trebuie să scrie manual migrarea pentru fiecare modificare a Entity. Începând cu versiunea Room 2.4.0, este disponibilă funcția experimentală autoMigrations pentru generarea automată a migrărilor.

Migrări automate cu autoMigrations

Funcția autoMigrations permite Room să genereze automat migrări pe baza diferențelor dintre versiunile Entity. Pentru utilizare, este suficient să adăugați adnotarea @AutoMigration în @Database și să specificați exportul schemei în JSON. Room compară schemele versiunilor vecine și generează interogările ALTER necesare. Totuși, autoMigrations suportă doar modificări compatibile invers: adăugarea de coloane, crearea de indici și modificarea tipurilor cu conversii compatibile.

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()

La adăugarea modificărilor complexe, cum ar fi redenumirea coloanelor sau îmbinarea tabelelor, este necesară migrarea manuală cu ajutorul tabelelor intermediare. Scenariu tipic: creați un tabel temporar cu schema veche, copiați datele din tabelul vechi în cel nou cu conversii, ștergeți tabelul vechi și redenumiți tabelul temporar. Room garantează că toate migrările sunt executate într-o singură tranzacție, iar la apariția unei erori, modificările sunt complet anulate.

Întrebări frecvente

Cu ce se deosebește Room de SQLiteOpenHelper?

Room oferă o abstractizare ORM cu adnotări și verificare SQL în faza de compilare, în timp ce SQLiteOpenHelper necesită scrierea manuală a tuturor interogărilor și gestionarea conexiunii. Room generează automat codul pentru operații CRUD și se integrează cu componentele arhitecturale Android, inclusiv LiveData și Flow.

Ce tipuri de date suportă Room?

Room suportă toate tipurile primitive Java: Int, Long, Boolean, Float, Double, precum și String, ByteArray și Date. Pentru tipurile complexe, cum ar fi List sau Enum, se folosesc TypeConverters — metode statice de conversie care transformă tipurile nestandard în formate suportate de SQLite.

Se poate folosi Room fără corutine?

Da, Room suportă apeluri sincrone fără corutine, dar acestea blochează firul de execuție în care rulează. Pentru lucrul asincron se poate folosi LiveData sau RxJava în locul corutinelor. Google recomandă utilizarea corutinelor ca metodă principală de acces asincron la date în proiecte noi.

Ce se întâmplă în lipsa migrării?

Dacă Room detectează o nepotrivire a versiunii bazei de date și nu găsește o migrare potrivită, în mod implicit apare IllegalStateException cu descrierea erorii. Dezvoltatorul poate suprascrie acest comportament prin metoda fallbackToDestructiveMigration, care va șterge baza existentă și va crea una nouă cu pierderea tuturor datelor.

Cum gestionează Room relațiile între tabele?

Room suportă relații prin obiecte înglobate cu adnotarea @Embedded și prin clase de relații cu adnotarea @Relation. Pentru interogări complexe cu îmbinarea tabelelor se folosesc clase POJO personalizate, ale căror câmpuri sunt populate din rezultatele @Query cu operatorul JOIN în SQL.

Concluzii

  • Room — bibliotecă ORM Android Jetpack care creează un strat de abstractizare peste SQLite pentru stocarea convenabilă a datelor pe dispozitiv.
  • Arhitectura se bazează pe trei componente: Entity (schema tabelului), DAO (operații) și Database (punct de intrare).
  • Verificarea interogărilor SQL în faza de compilare — principalul avantaj care elimină erorile de runtime în interogări.
  • Suportul încorporat pentru Flow, LiveData și RxJava permite construirea de arhitecturi reactive cu actualizare automată a UI la modificarea datelor.
  • Migrările în Room asigură actualizarea fără probleme a schemei bazei de date fără pierderea informațiilor salvate ale utilizatorului.
  • Biblioteca se integrează cu corutinele Kotlin prin funcții suspend, simplificând lucrul asincron cu datele.
  • Pentru proiecte noi, Room este soluția recomandată oficial de Google pentru stocarea locală a datelor pe Android.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și