Room: cos'è, libreria ORM e lavoro con SQLite

Autore: IT Sectr Pubblicato: 2026-03-12 Tempo di lettura: 10 min

Room è una libreria ORM di Android Jetpack che fornisce un livello di astrazione su SQLite per lavorare con database locali su Android. Secondo la documentazione ufficiale su Android Developers, 2025, Room genera automaticamente implementazioni DAO basate su annotazioni in fase di compilazione, eliminando circa il 70% del codice boilerplate rispetto all'uso diretto di SQLiteOpenHelper. La libreria esegue la validazione delle query SQL in fase di compilazione, consentendo di individuare errori di sintassi prima di eseguire l'applicazione su un dispositivo.

Punti Chiave

  • Room è una libreria ORM di Android Jetpack che fornisce un livello di astrazione su SQLite per l'archiviazione locale dei dati nelle applicazioni Android.
  • Tre componenti principali: Entity (definizione tabella), DAO (operazioni sui dati) e Database (punto di ingresso del database).
  • La validazione delle query SQL in fase di compilazione è un vantaggio chiave, consentendo di trovare errori prima dell'installazione dell'app.
  • Supporto integrato per Flow, LiveData e RxJava per l'osservazione reattiva delle modifiche nel database.
  • Il meccanismo di migrazioni consente di aggiornare lo schema del database senza perdere i dati utente già salvati.

Cos'è la libreria Room ORM?

Room è una libreria ORM di Android Jetpack creata da Google per semplificare il lavoro con database SQLite locali sulla piattaforma Android. Fornisce annotazioni per descrivere lo schema dei dati e genera automaticamente implementazioni di interfacce DAO in fase di compilazione. A differenza dell'uso diretto di SQLiteOpenHelper, Room libera lo sviluppatore dalla scrittura di una quantità significativa di codice boilerplate per creare, aprire e gestire le connessioni al database.

La libreria è stata presentata al Google I/O 2017 come parte dei componenti dell'architettura Android. Da allora, Room è diventato lo standard de facto per l'archiviazione locale dei dati, superando in popolarità soluzioni come GreenDAO e Realm per Android. Secondo Google, la libreria è utilizzata in oltre il 60% delle applicazioni pubblicate su Google Play che lavorano con dati locali sul dispositivo.

La caratteristica principale è la validazione delle query SQL in fase di compilazione tramite un processore di annotazioni. Se uno sviluppatore commette un errore in un comando SQL, ad esempio specificando un nome di colonna inesistente, la build fallirà con un errore prima dell'installazione dell'applicazione. Questo è fondamentalmente diverso dall'approccio SQLiteOpenHelper, dove tali errori vengono rilevati solo in fase di esecuzione, spesso in produzione.

TypeConverters per tipi non standard

SQLite supporta solo cinque tipi di dati: TEXT, INTEGER, REAL, BLOB e NULL. Tuttavia, Java e Kotlin utilizzano tipi complessi: Date, List, Enum e oggetti personalizzati. Per memorizzarli, Room fornisce il meccanismo TypeConverters — metodi statici che convertono un tipo complesso in un tipo primitivo comprensibile da SQLite. Ad esempio, un oggetto Date viene convertito in Long (timestamp) e List<String> in una stringa JSON tramite Gson o 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()

Per dichiarare un convertitore, è sufficiente aggiungere l'annotazione @TypeConverter a un metodo statico e specificare la classe del convertitore nell'annotazione @TypeConverters a livello di database. Room applica automaticamente il convertitore durante la lettura e scrittura del tipo corrispondente in ogni query SQL senza dover chiamare manualmente i metodi di conversione.

Architettura di Room: tre componenti chiave

Room è composto da tre componenti principali: Entity, DAO e Database. Ciascuno svolge un ruolo strettamente definito ed è annotato con l'annotazione corrispondente. Insieme, formano un livello completo di accesso ai dati che isola la logica di business dell'applicazione dai dettagli di implementazione di SQLite.

Entity — Tabella del database

Entity è una classe di dati che descrive la struttura di una tabella nel database. Ogni campo della classe corrisponde a una colonna della tabella e ogni riga nel database corrisponde a un'istanza della classe. L'annotazione @Entity indica a Room che la classe è una tabella. Il campo con l'annotazione @PrimaryKey definisce la chiave primaria, che può essere auto-incrementante o composta. @ForeignKey viene utilizzato per le relazioni tra tabelle, garantendo l'integrità dei dati a livello di database.

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 — Operazioni sui dati

DAO (Data Access Object) è un'interfaccia o classe astratta che dichiara operazioni per lavorare con i dati: inserimento, lettura, aggiornamento ed eliminazione. Ogni operazione è annotata con @Insert, @Query, @Update o @Delete. Room genera automaticamente l'implementazione di questa interfaccia in fase di compilazione. L'annotazione @Query è particolarmente preziosa — accetta una query SQL come stringa e ne valida la correttezza in fase di build.

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 — Punto di ingresso

Database è una classe astratta che estende RoomDatabase e funge da punto di ingresso al database. Contiene un elenco di tutte le Entities e fornisce metodi astratti per ottenere DAO. La classe è annotata con @Database, che specifica la versione dello schema e l'elenco delle entità. L'istanza del database viene creata tramite Room.databaseBuilder con il contesto dell'applicazione, il nome del file e la classe Database.

Come Room funziona con SQLite sotto il cofano

Room non sostituisce SQLite, ma funziona sopra di esso come livello di astrazione. L'architettura interna include un processore di annotazioni, un generatore di codice e un pool di connessioni. In fase di compilazione, il processore di annotazioni analizza le classi Entity, DAO e Database, quindi genera classi di implementazione con il suffisso _Impl. Tutte le classi generate vengono inserite nel pacchetto di build e non sono visibili direttamente allo sviluppatore.

La generazione di codice in fase di compilazione è il meccanismo centrale di Room. Per ogni interfaccia DAO, viene generata una classe con l'implementazione completa di tutti i metodi annotati. Le query SQL dall'annotazione @Query vengono validate per correttezza: il processore abbina i nomi delle colonne con i campi Entity e controlla la sintassi SQL. Se viene trovato un errore, la build viene interrotta con un messaggio chiaro. Questo è impossibile quando si utilizza SQLiteOpenHelper diretto, dove gli errori si manifestano solo in fase di esecuzione.

Generazione di codice in fase di compilazione

Il processo di generazione comprende tre fasi. Prima — validazione dello schema: il processore verifica che tutte le classi elencate in @Database siano Entities valide. Seconda — generazione del corpo DAO: per ogni metodo, viene creata un'implementazione utilizzando l'oggetto interno RoomSQLiteQuery che esegue query preparate. Terza — generazione della classe Database_Impl, che gestisce la creazione e l'apertura del database, nonché l'inizializzazione di tutti gli oggetti 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 non crea un pool di thread separato per le operazioni di database. Per impostazione predefinita, le query vengono eseguite nel thread chiamante con una limitazione: lettura e scrittura bloccano il thread. Per il lavoro asincrono, Room si integra con le coroutine Kotlin tramite funzioni suspend, con LiveData tramite valori di ritorno e con Flow tramite wrapper reattivi. Questo offre allo sviluppatore la flessibilità di scegliere la soluzione architetturale per un compito specifico.

Esempio di utilizzo di Room in un'applicazione Android

Vediamo un esempio pratico di creazione di un'applicazione per prendere appunti utilizzando Room. L'applicazione contiene una tabella Note con i campi id, title, content e timestamp. Gli utenti possono aggiungere, visualizzare ed eliminare note. Le coroutine vengono utilizzate per le operazioni asincrone.

Configurazione delle dipendenze Gradle

Per integrare Room in un progetto Android, aggiungi le dipendenze al file build.gradle del modulo dell'applicazione. Room richiede tre componenti: la libreria runtime, il processore di annotazioni kapt e il supporto opzionale per le coroutine. La versione della libreria è specificata in una variabile room_version per facilitare l'aggiornamento. A partire da Room 2.4.0, KSP è supportato come alternativa a kapt con velocità di compilazione più elevate.

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"
    // Opzionale: test
    testImplementation "androidx.room:room-testing:$room_version"
}

Dopo aver configurato le dipendenze, crea tre file: l'Entity Note, l'interfaccia NoteDao e la classe AppDatabase. L'Entity Note contiene campi con annotazioni @PrimaryKey e @ColumnInfo. Il DAO fornisce metodi per inserire, ottenere l'elenco ed eliminare. Il Database collega l'Entity e il DAO tramite l'annotazione @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)
}

Il file AppDatabase viene dichiarato come una classe astratta che estende RoomDatabase. L'annotazione @Database specifica tutte le Entities per la versione corrente e il numero di versione dello schema. Per ottenere un'istanza, viene utilizzato il pattern singleton tramite il metodo build di Room.databaseBuilder con il contesto dell'applicazione. La memorizzazione nella cache dell'istanza del database previene creazioni multiple che potrebbero causare perdite di memoria.

Migrazioni del database in Room

Le Migrazioni in Room sono un meccanismo per modificare lo schema del database durante l'aggiornamento di un'applicazione senza perdere i dati esistenti. Quando un utente installa una nuova versione con Entities modificate, Room rileva la discrepanza di versione ed esegue i passaggi di migrazione specificati. Senza una migrazione, il database verrà eliminato e ricreato, con conseguente perdita di tutti i dati salvati dall'utente.

Una migrazione è descritta dalla classe Migration, che prende le versioni iniziale e finale del database. All'interno del metodo migrate, viene eseguita una query SQL ALTER TABLE o CREATE TABLE per modificare lo schema. Room non è in grado di rilevare automaticamente le modifiche dello schema — lo sviluppatore deve scrivere una migrazione manualmente per ogni modifica dell'Entity. A partire da Room 2.4.0, è disponibile la funzionalità sperimentale autoMigrations per la generazione automatica delle migrazioni.

Migrazioni automatiche con autoMigrations

La funzionalità autoMigrations consente a Room di generare automaticamente migrazioni basate sulle differenze tra le versioni dell'Entity. Per utilizzarla, è sufficiente aggiungere l'annotazione @AutoMigration a @Database e abilitare l'esportazione dello schema in JSON. Room confronta gli schemi delle versioni adiacenti e genera le query ALTER necessarie. Tuttavia, autoMigrations supporta solo modifiche retrocompatibili: aggiunta di colonne, creazione di indici e modifica di tipi con trasformazioni compatibili.

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

Per modifiche complesse, come la rinomina di colonne o l'unione di tabelle, è necessaria una migrazione manuale utilizzando tabelle intermedie. Uno scenario tipico: creare una tabella temporanea con il vecchio schema, copiare i dati dalla vecchia tabella alla nuova con trasformazioni, eliminare la vecchia tabella e rinominare quella temporanea. Room garantisce che tutte le migrazioni vengano eseguite in un'unica transazione e, in caso di errore, le modifiche vengono completamente annullate.

Domande Frequenti

In cosa Room si differenzia da SQLiteOpenHelper?

Room fornisce un'astrazione ORM con annotazioni e validazione SQL in fase di compilazione, mentre SQLiteOpenHelper richiede la scrittura manuale di tutte le query e la gestione delle connessioni. Room genera automaticamente codice per le operazioni CRUD e si integra con i componenti dell'architettura Android, inclusi LiveData e Flow.

Quali tipi di dati supporta Room?

Room supporta tutti i tipi primitivi Java: Int, Long, Boolean, Float, Double, oltre a String, ByteArray e Date. Per tipi complessi come List o Enum, vengono utilizzati TypeConverters — metodi di conversione statici che convertono tipi non standard in formati compatibili con SQLite.

Si può usare Room senza coroutine?

Sì, Room supporta chiamate sincrone senza coroutine, ma bloccano il thread in cui vengono eseguite. Per il lavoro asincrono, puoi utilizzare LiveData o RxJava invece delle coroutine. Google raccomanda di utilizzare le coroutine come metodo principale per l'accesso asincrono ai dati nei nuovi progetti.

Cosa succede in assenza di migrazione?

Se Room rileva una discrepanza nella versione del database e non trova una migrazione adeguata, per impostazione predefinita genera un'IllegalStateException con una descrizione dell'errore. Lo sviluppatore può sovrascrivere questo comportamento con il metodo fallbackToDestructiveMigration, che eliminerà il database esistente e ne creerà uno nuovo, perdendo tutti i dati.

Come gestisce Room le relazioni tra tabelle?

Room supporta le relazioni tramite oggetti annidati con l'annotazione @Embedded e tramite classi di relazione con l'annotazione @Relation. Per query complesse che coinvolgono join di tabelle, vengono utilizzate classi POJO personalizzate, i cui campi vengono popolati dai risultati di @Query con istruzioni SQL JOIN.

Riepilogo

  • Room è una libreria ORM di Android Jetpack che crea un livello di astrazione su SQLite per l'archiviazione conveniente dei dati sul dispositivo.
  • L'architettura si basa su tre componenti: Entity (schema tabella), DAO (operazioni) e Database (punto di ingresso).
  • La validazione delle query SQL in fase di compilazione è il principale vantaggio, eliminando gli errori di runtime nelle query.
  • Il supporto integrato per Flow, LiveData e RxJava consente di costruire architetture reattive con aggiornamenti automatici dell'interfaccia utente quando i dati cambiano.
  • Le migrazioni in Room garantiscono aggiornamenti senza interruzioni dello schema del database senza perdere le informazioni salvate dell'utente.
  • La libreria si integra con le coroutine Kotlin tramite funzioni suspend, semplificando il lavoro asincrono con i dati.
  • Per i nuovi progetti, Room è la soluzione ufficialmente raccomandata da Google per l'archiviazione locale dei dati su Android.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche