Da SQLite eine relationale Datenbank ist, können Sie Beziehungen zwischen Entitäten. Während die meisten objektrelationalen Mapping-Bibliotheken -Objekte verweisen aufeinander, wird von Room ausdrücklich untersagt. Weitere Informationen zu der technischen Begründung für diese Entscheidung unter Warum Objektverweise zulassen.
Zwei mögliche Ansätze
In Room gibt es zwei Möglichkeiten, eine Beziehung zwischen Entitäten zu definieren und abzufragen: mit einer Zwischendatenklasse mit eingebettete Objekte oder eine relationale Abfragemethode mit Multimap-Rückgabe Typ.
Datenmittelklasse
Bei dieser Methode definieren Sie eine Datenklasse, die die Beziehung zwischen den Raumentitäten. Diese Datenklasse enthält die Paare zwischen Instanzen einer Entität und Instanzen einer anderen Entität als eingebettet Objekte. Ihre Abfragemethoden können dann Instanzen dieses Datenklasse für die Verwendung in Ihrer App.
Sie können beispielsweise eine UserBook
-Datenklasse definieren, um Bibliotheksnutzer zu repräsentieren.
die bestimmte Bücher ausgecheckt haben, und definieren Sie eine Abfragemethode, um eine Liste
UserBook
Instanzen aus der Datenbank:
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; }
Rückgabetypen für mehrere Karten
Beim Ansatz des Multimap-Rückgabetyps müssen Sie keine zusätzlichen Datenklassen verwendet werden. Stattdessen definieren Sie eine multimap-Rückgabetyp für Ihre -Methode basierend auf der gewünschten Kartenstruktur und definieren Sie die Beziehung zwischen den Entitäten direkt in der SQL-Abfrage.
Die folgende Abfragemethode gibt beispielsweise eine Zuordnung von User
und Book
zurück.
-Instanzen zur Darstellung von Bibliotheksnutzern, bei denen bestimmte Bücher gekauft wurden:
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();
Ansatz auswählen
Der Raum unterstützt beide Ansätze. Sie können also je nachdem, welcher Ansatz für Ihre App am besten geeignet ist. In diesem Abschnitt werden einige der warum Sie vielleicht das eine oder das andere bevorzugen.
Mit dem Ansatz für die Zwischendatenklasse müssen Sie das Schreiben von komplexen SQL-Anweisungen vermeiden. abfragen, kann aber auch die Codekomplexität erhöhen, da die zusätzliche Datenklassen zu erstellen. Kurz gesagt: Der Rückgabetyp „Multimap“ erfordert, dass Ihre SQL-Abfragen mehr Arbeit leisten und die Zwischendaten erfordert Ihren Code mehr Arbeit.
Wenn Sie keinen bestimmten Grund für die Verwendung von Datenklassen der mittleren Stufe haben, empfehlen wir den Multimap-Rückgabetyp. Weitere Informationen über erhalten Sie unter Eine Conversion zurückgeben Multimap.
Im weiteren Verlauf dieses Leitfadens wird gezeigt, wie Beziehungen mithilfe der mit Datenklassen für Fortgeschrittene.
Eingebettete Objekte erstellen
Manchmal möchten Sie eine Entität oder ein Datenobjekt als
in Ihrer Datenbanklogik ein zusammenhängendes Ganzes, auch wenn das Objekt mehrere
. In diesen Fällen können Sie das
@Embedded
-Annotation zur Darstellung eines Objekts, das Sie in seine
untergeordneten Feldern einer Tabelle. Sie können dann die eingebetteten Felder genau wie gewohnt abfragen.
für andere einzelne Spalten.
Die Klasse User
kann beispielsweise ein Feld vom Typ Address
enthalten, das
stellt eine Zusammensetzung von Feldern mit den Namen street
, city
, state
und
postCode
Um die zusammengesetzten Spalten separat in der Tabelle zu speichern, fügen Sie ein
Feld Address
in der Klasse User
, das mit annotiert ist
@Embedded
, als
wie im folgenden Code-Snippet dargestellt:
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; }
Die Tabelle, die ein User
-Objekt darstellt, enthält dann Spalten mit Folgendem:
Namen: id
, firstName
, street
, state
, city
und post_code
.
Wenn eine Entität über mehrere eingebettete Felder desselben Typs verfügt, können Sie jedes Feld beibehalten
eindeutig sein, indem Sie den Parameter
prefix
Property. Der angegebene Wert wird dann am Anfang jeder Spalte hinzugefügt
Namen im eingebetteten Objekt ein.
1:1-Beziehungen definieren
Eine 1:1-Beziehung zwischen zwei Entitäten ist eine Beziehung, bei der jede Instanz der übergeordneten Entität entspricht genau einer Instanz der untergeordneten Entität und umgekehrt.
Stellen Sie sich beispielsweise eine Musik-Streaming-App vor, bei der der Nutzer über eine Bibliothek mit
eigenen Songs. Jeder Nutzer hat nur eine Bibliothek und jede Bibliothek
genau einem Nutzer entspricht. Daher gibt es ein Einzelgespräch
Beziehung zwischen der User
- und der Library
-Entität.
Um eine 1:1-Beziehung zu definieren, erstellen Sie zunächst eine Klasse für jede Ihrer beiden Entitäten. Eine der Entitäten muss eine Variable enthalten, die auf den Primärschlüssel der anderen Entität verweist.
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: String, val age: Int ) @Entity data class Library( @PrimaryKey val libraryId: Long, val userOwnerId: Long )
Java
@Entity public class User { @PrimaryKey public long userId; public String name; public int age; } @Entity public class Library { @PrimaryKey public long libraryId; public long userOwnerId; }
Um die Liste der Nutzer und entsprechenden Bibliotheken abzufragen, müssen Sie zuerst
um die 1:1-Beziehung
zwischen den beiden Entitäten zu modellieren. Erstellen Sie dazu
Neue Datenklasse, bei der jede Instanz eine Instanz der übergeordneten Entität und
die entsprechende Instanz der untergeordneten Entität. Fügen Sie den @Relation
hinzu.
an die Instanz der untergeordneten Entität übergeben, wobei parentColumn
auf
den Namen der Primärschlüsselspalte der übergeordneten Entität und entityColumn
ist auf den Namen der Spalte der untergeordneten Entität festgelegt, die auf die übergeordnete Entität verweist.
Primärschlüssel der Entität.
Kotlin
data class UserAndLibrary( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) val library: Library )
Java
public class UserAndLibrary { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) public Library library; }
Fügen Sie abschließend der DAO-Klasse eine Methode hinzu, die alle Instanzen der Daten zurückgibt
Klasse, die die übergeordnete und die untergeordnete Entität koppelt. Diese Methode erfordert
Raum zum Ausführen von zwei Abfragen, also fügen Sie hier die Annotation @Transaction
hinzu
damit der gesamte Vorgang atomar ausgeführt wird.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersAndLibraries(): List<UserAndLibrary>
Java
@Transaction @Query("SELECT * FROM User") public List<UserAndLibrary> getUsersAndLibraries();
1:n-Beziehungen definieren
Bei einer 1:n-Beziehung zwischen zwei Entitäten Instanz der übergeordneten Entität entspricht null oder mehr Instanzen der untergeordneten Entität Entität. Jede Instanz der untergeordneten Entität kann jedoch nur genau einer Entität entsprechen. Instanz der übergeordneten Entität.
Nehmen wir im Beispiel der Musik-Streaming-App an, dass die Nutzenden die Möglichkeit haben,
ihre Songs in Playlists zusammenfassen. Jeder Nutzer kann beliebig viele Playlists erstellen.
aber jede Playlist wird
von genau einem Nutzer erstellt. Daher gibt es eine
1:n-Beziehung zwischen der User
-Entität und der Playlist
-Entität.
Um eine 1:n-Beziehung zu definieren, erstellen Sie zunächst eine Klasse für die beiden Entitäten. Wie bei einer 1:1-Beziehung muss die untergeordnete Entität eine Variable enthalten, die ist ein Verweis auf den Primärschlüssel der übergeordneten Entität.
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: String, val age: Int ) @Entity data class Playlist( @PrimaryKey val playlistId: Long, val userCreatorId: Long, val playlistName: String )
Java
@Entity public class User { @PrimaryKey public long userId; public String name; public int age; } @Entity public class Playlist { @PrimaryKey public long playlistId; public long userCreatorId; public String playlistName; }
Um die Liste der Nutzer und der entsprechenden Playlists abzufragen, musst du zuerst
um die 1:n-Beziehung
zwischen den beiden Entitäten zu modellieren. Erstellen Sie dazu
eine neue Datenklasse, bei der jede Instanz eine Instanz der übergeordneten Entität und
Liste aller entsprechenden Instanzen der untergeordneten Entität. Fügen Sie den @Relation
hinzu.
an die Instanz der untergeordneten Entität übergeben, wobei parentColumn
auf
den Namen der Primärschlüsselspalte der übergeordneten Entität und entityColumn
ist auf den Namen der Spalte der untergeordneten Entität festgelegt, die auf die übergeordnete Entität verweist.
Primärschlüssel der Entität.
Kotlin
data class UserWithPlaylists( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: List<Playlist> )
Java
public class UserWithPlaylists { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) public List<Playlist> playlists; }
Fügen Sie abschließend der DAO-Klasse eine Methode hinzu, die alle Instanzen der Daten zurückgibt
Klasse, die die übergeordnete und die untergeordnete Entität koppelt. Diese Methode erfordert
Raum zum Ausführen von zwei Abfragen, also fügen Sie hier die Annotation @Transaction
hinzu
damit der gesamte Vorgang atomar ausgeführt wird.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylists(): List<UserWithPlaylists>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylists> getUsersWithPlaylists();
m:n-Beziehungen definieren
Bei einer n:n-Beziehung zwischen zwei Entitäten Instanz der übergeordneten Entität entspricht null oder mehr Instanzen der untergeordneten Entität und umgekehrt.
Betrachten Sie im Beispiel der Musikstreaming-App die Titel in den benutzerdefinierten Playlists.
Jede Playlist kann viele Songs enthalten und jeder Song kann Teil von vielen
verschiedene Playlists erstellen. Daher besteht eine n:n-Beziehung
zwischen der Playlist
- und der Song
-Entität.
Um eine n:n-Beziehung zu definieren, erstellen Sie zunächst eine Klasse für jede Ihrer beiden
Entitäten. N-zu-N-Beziehungen
unterscheiden sich von anderen Beziehungstypen, da in der Regel
Verweis auf die übergeordnete Entität in der untergeordneten Entität Erstellen Sie stattdessen eine dritte
Klasse zur Darstellung einer assoziativen Entität oder eines Querverweises
zwischen den beiden Entitäten einfügen. Die Querverweistabelle muss Spalten für
Primärschlüssel jeder Entität in der m:n-Beziehung, die in
in der Tabelle. In diesem Beispiel entspricht jede Zeile in der Vergleichstabelle
ein Paar aus einer Playlist
-Instanz und einer Song
-Instanz, wobei die referenzierte Instanz
Song ist in der entsprechenden Playlist enthalten.
Kotlin
@Entity data class Playlist( @PrimaryKey val playlistId: Long, val playlistName: String ) @Entity data class Song( @PrimaryKey val songId: Long, val songName: String, val artist: String ) @Entity(primaryKeys = ["playlistId", "songId"]) data class PlaylistSongCrossRef( val playlistId: Long, val songId: Long )
Java
@Entity public class Playlist { @PrimaryKey public long playlistId; public String playlistName; } @Entity public class Song { @PrimaryKey public long songId; public String songName; public String artist; } @Entity(primaryKeys = {"playlistId", "songId"}) public class PlaylistSongCrossRef { public long playlistId; public long songId; }
Der nächste Schritt hängt davon ab, wie Sie diese verwandten Entitäten abfragen möchten.
- Wenn du Playlists und eine Liste der entsprechenden Songs für eine Playlist abfragen möchtest,
Erstellen Sie für jede Playlist eine neue Datenklasse, die ein einzelnes
Playlist
-Objekt enthält. und eine Liste allerSong
-Objekte, die die Playlist enthält. - Wenn du nur Songs und eine Liste der entsprechenden Playlists abfragen möchtest,
Erstellen Sie jeweils eine neue Datenklasse, die ein einzelnes
Song
-Objekt und eine ListePlaylist
-Objekten enthält, in dem der Song vorkommt.
In beiden Fällen modellieren Sie die Beziehung zwischen den Entitäten mithilfe der Funktion
associateBy
-Eigenschaft in der Annotation @Relation
in den folgenden
diese Klassen, um die Querverweisentität zu identifizieren, die die Beziehung bereitstellt.
zwischen der Playlist
- und der Song
-Entität.
Kotlin
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> ) data class SongWithPlaylists( @Embedded val song: Song, @Relation( parentColumn = "songId", entityColumn = "playlistId", associateBy = Junction(PlaylistSongCrossRef::class) ) val playlists: List<Playlist> )
Java
public class PlaylistWithSongs { @Embedded public Playlist playlist; @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = @Junction(PlaylistSongCrossref.class) ) public List<Song> songs; } public class SongWithPlaylists { @Embedded public Song song; @Relation( parentColumn = "songId", entityColumn = "playlistId", associateBy = @Junction(PlaylistSongCrossref.class) ) public List<Playlist> playlists; }
Fügen Sie schließlich der DAO-Klasse eine Methode hinzu, um die Abfragefunktionalität App-Anforderungen.
getPlaylistsWithSongs
: Diese Methode fragt die Datenbank ab und gibt alle Die resultierendenPlaylistWithSongs
-Objekte.getSongsWithPlaylists
: Diese Methode fragt die Datenbank ab und gibt alle Die resultierendenSongWithPlaylists
-Objekte.
Für diese Methoden muss Room jeweils zwei Abfragen ausführen. Fügen Sie also den Parameter
@Transaction
-Annotation zu beiden Methoden hinzufügen, sodass die gesamte
wird in kleinstmöglichen Schritten ausgeführt.
Kotlin
@Transaction @Query("SELECT * FROM Playlist") fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") fun getSongsWithPlaylists(): List<SongWithPlaylists>
Java
@Transaction @Query("SELECT * FROM Playlist") public List<PlaylistWithSongs> getPlaylistsWithSongs(); @Transaction @Query("SELECT * FROM Song") public List<SongWithPlaylists> getSongsWithPlaylists();
Verschachtelte Beziehungen definieren
Manchmal müssen Sie eine Reihe von drei oder mehr Tabellen abfragen, die zusammengehören. In diesem Fall definierst du verschachtelte Beziehungen. zwischen den Tabellen.
Angenommen, Sie möchten in der Musik-Streaming-App alle Nutzer, alle Playlists für jeden Nutzer und alle Songs in jeder Playlist für jeden Nutzer. Nutzer haben eine 1:n-Beziehung mit Playlists und Playlists eine m:n-Beziehung mit Songs. Im folgenden Codebeispiel sehen Sie die Klassen, die diese Entitäten sowie in der Vergleichstabelle für die m:n-Beziehung zwischen Playlists und Songs:
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: String, val age: Int ) @Entity data class Playlist( @PrimaryKey val playlistId: Long, val userCreatorId: Long, val playlistName: String ) @Entity data class Song( @PrimaryKey val songId: Long, val songName: String, val artist: String ) @Entity(primaryKeys = ["playlistId", "songId"]) data class PlaylistSongCrossRef( val playlistId: Long, val songId: Long )
Java
@Entity public class User { @PrimaryKey public long userId; public String name; public int age; } @Entity public class Playlist { @PrimaryKey public long playlistId; public long userCreatorId; public String playlistName; } @Entity public class Song { @PrimaryKey public long songId; public String songName; public String artist; } @Entity(primaryKeys = {"playlistId", "songId"}) public class PlaylistSongCrossRef { public long playlistId; public long songId; }
Modellieren Sie zunächst die Beziehung zwischen zwei Tabellen in Ihrem Dataset,
mit einer Datenklasse und dem
@Relation
-Anmerkung. Die
Das folgende Beispiel zeigt eine PlaylistWithSongs
-Klasse, die eine m:n-Beziehung modelliert
Beziehung zwischen der Playlist
-Entitätsklasse und der Song
-Entitätsklasse:
Kotlin
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: List<Song> )
Java
public class PlaylistWithSongs { @Embedded public Playlist playlist; @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef.class) ) public List<Song> songs; }
Nachdem Sie eine Datenklasse definiert haben, die diese Beziehung darstellt, erstellen Sie eine weitere
Datenklasse, die die Beziehung zwischen einer anderen Tabelle aus Ihrem Dataset und
die erste Beziehungsklasse,
„Verschachtelung“, Beziehung innerhalb des neuen
eins. Das folgende Beispiel zeigt eine UserWithPlaylistsAndSongs
-Klasse, die ein Modell
eine 1:n-Beziehung zwischen der User
-Entitätsklasse und dem
PlaylistWithSongs
-Beziehungsklasse:
Kotlin
data class UserWithPlaylistsAndSongs( @Embedded val user: User @Relation( entity = Playlist::class, parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: List<PlaylistWithSongs> )
Java
public class UserWithPlaylistsAndSongs { @Embedded public User user; @Relation( entity = Playlist.class, parentColumn = "userId", entityColumn = "userCreatorId" ) public List<PlaylistWithSongs> playlists; }
Die UserWithPlaylistsAndSongs
-Klasse modelliert die Beziehungen indirekt.
zwischen den drei Entitätsklassen User
, Playlist
und Song
. Dies ist
wie in Abbildung 1 dargestellt.
![UserWithPlaylistsAndSongs modelliert die Beziehung zwischen User und
„PlaylistWithSongs“, das wiederum die Beziehung zwischen „Playlist“
und Song.](https://developer.android.com/static/images/training/data-storage/room_nested_relationships.png?authuser=9&hl=de)
Abbildung 1: Diagramm der Beziehungsklassen in der Beispiel für eine Musikstreaming-App
Wenn Ihr Dataset weitere Tabellen enthält, erstellen Sie eine Klasse zur Modellierung der Beziehung zwischen jeder verbleibenden Tabelle und der Beziehungsklasse, die modelliert, Beziehungen zwischen allen vorherigen Tabellen. Dadurch wird eine Kette verschachtelter Beziehungen zwischen allen Tabellen, die Sie abfragen möchten.
Fügen Sie schließlich der DAO-Klasse eine Methode hinzu, um die Abfragefunktionalität anzuzeigen, die
die Ihre App braucht. Bei dieser Methode muss Room mehrere Abfragen ausführen. Fügen Sie also den Parameter
@Transaction
-Anmerkung
damit der gesamte Vorgang atomar ausgeführt wird:
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();
Weitere Informationen
Weitere Informationen zum Definieren von Beziehungen zwischen Entitäten in Room finden Sie in der wenn Sie zusätzliche Ressourcen nutzen.
Produktproben
Videos
- Neu in Room (Android Dev Summit 2019)