La stanza virtuale offre funzionalità per la conversione da tipi primitivi a formati confezionati ma non consentono riferimenti agli oggetti tra entità. Questo documento spiega come utilizzare i convertitori dei tipi e perché Room non supporta gli oggetti riferimenti.
Utilizzare convertitori dei tipi
A volte, hai bisogno che l'app archivi un tipo di dati personalizzato in un unico database
colonna. Supporti i tipi personalizzati fornendo i convertitori dei tipi, che sono
che indicano a Room come convertire i tipi personalizzati da e verso tipi noti che
La stanza può persistere. Per identificare gli utenti che hanno completato una conversione dei tipi utilizzando
@TypeConverter
.
Supponi di dover mantenere le istanze di Date
in
il database della stanza. La stanza virtuale non sa come rendere persistenti Date
oggetti, quindi devi
per definire i convertitori dei tipi:
Kotlin
class Converters { @TypeConverter fun fromTimestamp(value: Long?): Date? { return value?.let { Date(it) } } @TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time?.toLong() } }
Java
public class Converters { @TypeConverter public static Date fromTimestamp(Long value) { return value == null ? null : new Date(value); } @TypeConverter public static Long dateToTimestamp(Date date) { return date == null ? null : date.getTime(); } }
Questo esempio definisce due metodi del convertitore dei tipi: uno che converte un Date
in un oggetto Long
e uno che esegue la conversione inversa
Da Long
a Date
. Poiché la stanza virtuale sa come rendere persistenti Long
oggetti, può utilizzare
di questi convertitori in modo da rendere persistenti Date
oggetti.
In seguito, aggiungi la classe @TypeConverters
alla classe AppDatabase
in modo che la stanza sappia dell'utente che ha completato una conversione
che hai definito:
Kotlin
@Database(entities = [User::class], version = 1) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
Java
@Database(entities = {User.class}, version = 1) @TypeConverters({Converters.class}) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); }
Una volta definiti questi utenti che hanno completato una conversione, puoi utilizzare il tuo tipo personalizzato nel entità e DAO nello stesso modo in cui utilizzeresti i tipi primitivi:
Kotlin
@Entity data class User(private val birthday: Date?) @Dao interface UserDao { @Query("SELECT * FROM user WHERE birthday = :targetDate") fun findUsersBornOnDate(targetDate: Date): List<User> }
Java
@Entity public class User { private Date birthday; } @Dao public interface UserDao { @Query("SELECT * FROM user WHERE birthday = :targetDate") List<User> findUsersBornOnDate(Date targetDate); }
In questo esempio, Room può utilizzare ovunque il convertitore di tipi definito perché
annotato AppDatabase
con @TypeConverters
. Tuttavia, puoi anche digitare l'ambito
convertitori in entità o DAO specifiche annotando @Entity
o @Dao
corsi con @TypeConverters
.
Inizializzazione del convertitore dei tipi di controllo
Di solito, la stanza virtuale gestisce l'istanza dei convertitori dei tipi per te. Tuttavia,
a volte potresti dover passare ulteriori dipendenze al convertitore di tipi
il che significa che la tua app deve controllare direttamente l'inizializzazione
dei tuoi convertitori di tipo. In questo caso, annota la classe dell'utente che ha completato una conversione con
@ProvidedTypeConverter
:
Kotlin
@ProvidedTypeConverter class ExampleConverter { @TypeConverter fun StringToExample(string: String?): ExampleType? { ... } @TypeConverter fun ExampleToString(example: ExampleType?): String? { ... } }
Java
@ProvidedTypeConverter public class ExampleConverter { @TypeConverter public Example StringToExample(String string) { ... } @TypeConverter public String ExampleToString(Example example) { ... } }
Poi, oltre a dichiarare la classe dell'utente che ha completato una conversione in @TypeConverters
, utilizza
il
RoomDatabase.Builder.addTypeConverter()
per passare un'istanza della classe del convertitore a RoomDatabase
Builder:
Kotlin
val db = Room.databaseBuilder(...) .addTypeConverter(exampleConverterInstance) .build()
Java
AppDatabase db = Room.databaseBuilder(...) .addTypeConverter(exampleConverterInstance) .build();
Comprendere perché la stanza virtuale non consente riferimenti agli oggetti
Concetto chiave: La stanza virtuale non consente i riferimenti agli oggetti tra classi di entità. Devi invece richiedere esplicitamente i dati necessari alla tua app.
La mappatura delle relazioni da un database al rispettivo modello a oggetti è un'attività comune e funziona molto bene sul lato server. Anche quando il programma viene caricato campi al loro accesso, il server funziona comunque bene.
Tuttavia, sul lato client, questo tipo di caricamento lento non è fattibile perché di solito avviene nel thread dell'interfaccia utente e l'esecuzione di query sulle informazioni su disco nell'interfaccia utente del thread crea problemi di prestazioni significativi. Il thread dell'interfaccia utente in genere ha 16 ms per calcolare e tracciare il layout aggiornato di un'attività, in modo che anche se richiede solo 5 ms, è comunque probabile che per la tua app si esaurisca il tempo disegna l'inquadratura, causando evidenti problemi visivi. La query potrebbe richiedere anche tempo per il completamento se è in corso una transazione separata in parallelo oppure se il dispositivo esegue altre attività che usano molto disco. Se non utilizzi la funzionalità Lazy mentre viene caricato, tuttavia, l'app recupera più dati del necessario, creando memoria problemi di consumo.
Di solito le mappature relazionali degli oggetti lasciano questa decisione agli sviluppatori, possono fare tutto ciò che è meglio per i casi d'uso delle loro app. Gli sviluppatori di solito decidono di condividere il modello tra l'app e la UI. Questa soluzione non scalare bene, poiché man mano che la UI cambia nel tempo, il modello condiviso crea problemi difficili da prevedere ed eseguire il debug per gli sviluppatori.
Ad esempio, considera una UI che carica un elenco di Book
oggetti, con ogni libro
che dispone di un oggetto Author
. Inizialmente, potresti progettare le query in modo da utilizzare la funzione
Caricamento in corso per fare in modo che le istanze di Book
recuperino l'autore. Il primo recupero
Il campo author
esegue una query sul database. Qualche tempo dopo, ti rendi conto che
devono visualizzare il nome dell'autore
nell'interfaccia utente dell'app. Puoi accedere a questa
di nome con facilità, come mostrato nello snippet di codice riportato di seguito:
Kotlin
authorNameTextView.text = book.author.name
Java
authorNameTextView.setText(book.getAuthor().getName());
Tuttavia, questa modifica apparentemente innocua provoca l'esecuzione di query sulla tabella Author
sul thread principale.
Se esegui query anticipatamente sulle informazioni sull'autore, diventa difficile modificare
la modalità di caricamento dei dati se non ne hai più bisogno. Ad esempio, se la tua app
La UI non deve più mostrare le informazioni di Author
, l'app viene caricata in modo efficace
che non visualizza più, sprecando spazio in memoria preziosa. Della tua app
l'efficienza peggiora ulteriormente se la classe Author
fa riferimento a un'altra tabella,
ad esempio Books
.
Per fare riferimento a più entità contemporaneamente utilizzando una stanza virtuale, devi creare una POJO che contenga ogni entità, quindi scrivi una query che unisca tabelle. Questo modello ben strutturato, combinato con l'efficace query di ricerca Room di convalida, consente alla tua app di consumare meno risorse durante il caricamento migliorando le prestazioni e l'esperienza utente della tua app.