Scegliere i tipi di relazione tra gli oggetti

Poiché SQLite è un database relazionale, puoi definire le relazioni tra le entità. Tuttavia, mentre la maggior parte delle librerie di mappatura relazionale degli oggetti consente agli oggetti entità di far riferimento l'uno all'altro, Room lo vieta esplicitamente. Per conoscere il ragionamento tecnico alla base di questa decisione, consulta Informazioni sul motivo per cui Room non consente riferimenti agli oggetti.

Tipi di relazioni

Room supporta i seguenti tipi di relazioni:

  • One-to-one: rappresenta una relazione in cui una singola entità è correlata a un'altra singola entità.
  • Uno a molti: rappresenta una relazione in cui una singola entità può essere correlata a più entità di un altro tipo.
  • Molti a molti: rappresenta una relazione in cui più entità di un tipo possono essere associate a più entità di un altro tipo. In genere, è necessaria una tabella di join.
  • Relazioni nidificate (con oggetti incorporati): rappresenta una relazione in cui un'entità contiene un'altra entità come campo e questa entità nidificata può contenere ulteriormente altre entità. Viene utilizzata l'annotazione @Embedded.

Scegli tra due approcci

In Room esistono due modi per definire e eseguire query su una relazione tra entità. Puoi utilizzare:

  • Una classe di dati intermedia con oggetti incorporati oppure
  • Un metodo di query relazionale con un tipo di ritorno multimap.

Se non hai un motivo specifico per utilizzare classi di dati intermedie, ti consigliamo di utilizzare l'approccio del tipo di ritorno multimap. Per scoprire di più su questo approccio, consulta Restituire una mappa multipla.

L'approccio della classe di dati intermedia ti consente di evitare di scrivere query SQL complesse, ma può anche aumentare la complessità del codice perché richiede classi di dati aggiuntive. In breve, l'approccio del tipo di ritorno multimap richiede un maggiore impegno delle query SQL, mentre l'approccio della classe di dati intermedi richiede un maggiore impegno del codice.

Utilizzare l'approccio della classe di dati intermedi

Nell'approccio della classe di dati intermedia, definisci una classe di dati che modella la relazione tra le entità Room. Questa classe di dati contiene le associazioni tra istanze di un'entità e istanze di un'altra entità come oggetti incorporati. I metodi di query possono quindi restituire istanze di questa classe di dati per utilizzarle nella tua app.

Ad esempio, puoi definire una classe di dati UserBook per rappresentare gli utenti della biblioteca con libri specifici presi in prestito e definire un metodo di query per recuperare un elenco di istanze UserBook dal database:

Kotlin

@Dao
interface UserBookDao {
    @Query(
        "SELECT user.name AS userName, book.name AS bookName " +
        "FROM user, book " +
        "WHERE user.id = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String?, val bookName: String?)

Java

@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();
}

public class UserBook {
    public String userName;
    public String bookName;
}

Utilizza l'approccio ai tipi di ritorno multimap

Nell'approccio di tipo di ritorno multimap, non è necessario definire classi di dati aggiuntive. Devi invece definire un tipo di ritorno multimap per il tuo metodo in base alla struttura della mappa che preferisci e definire la relazione tra le entità direttamente nella query SQL.

Ad esempio, il seguente metodo di query restituisce una mappatura di istanze User e Book per rappresentare gli utenti della biblioteca che hanno preso in prestito libri specifici:

Kotlin

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

Java

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<User, List<Book>> loadUserAndBookNames();

Creare oggetti incorporati

A volte, vuoi esprimere un'entità o un oggetto dati come un insieme coerente nella logica del database, anche se l'oggetto contiene diversi campi. In queste situazioni, puoi utilizzare l'annotazione @Embedded per rappresentare un oggetto che vuoi decomporre nei relativi campi secondari all'interno di una tabella. Puoi quindi eseguire query sui campi incorporati come faresti per le altre singole colonne.

Ad esempio, la classe User può includere un campo di tipo Address che rappresenta una composizione di campi denominati street, city, state e postCode. Per archiviare le colonne composte separatamente nella tabella, includi un campoAddress. Dovrebbe apparire nella classe User annotata con @Embedded. Il seguente snippet di codice lo dimostra:

Kotlin

data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    @Embedded val address: Address?
)

Java

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code") public int postCode;
}

@Entity
public class User {
    @PrimaryKey public int id;

    public String firstName;

    @Embedded public Address address;
}

La tabella che rappresenta un oggetto User contiene quindi colonne con i seguenti nomi: id, firstName, street, state, city e post_code.

Se un'entità ha più campi incorporati dello stesso tipo, puoi mantenere univoca ogni colonna impostando la proprietà prefix. Room aggiunge quindi il valore fornito all'inizio di ogni nome di colonna nell'oggetto incorporato.

Risorse aggiuntive

Per scoprire di più sulla definizione delle relazioni tra entità in Room, consulta le seguenti risorse aggiuntive.

Video

Blog