Da SQLite eine relationale Datenbank ist, können Sie Beziehungen zwischen Entitäten definieren. Während die meisten Bibliotheken für die objektrelationale Zuordnung es ermöglichen, dass Entitätsobjekte aufeinander verweisen, ist dies in Room ausdrücklich verboten. Die technischen Gründe für diese Entscheidung finden Sie unter Warum sind Objektverweise in Room nicht zulässig?
Beziehungstypen
Room unterstützt die folgenden Beziehungstypen:
- 1:1: Stellt eine Beziehung dar, in der eine einzelne Entität mit einer anderen einzelnen Entität verknüpft ist.
- 1:n : Stellt eine Beziehung dar, in der eine einzelne Entität mit mehreren Entitäten eines anderen Typs verknüpft sein kann.
- n:m : Stellt eine Beziehung dar, in der mehrere Entitäten eines Typs mit mehreren Entitäten eines anderen Typs verknüpft sein können. Dazu ist in der Regel eine Verbindungstabelle erforderlich.
- Verschachtelte Beziehungen mit eingebetteten Objekten: Stellt eine
Beziehung dar, in der eine Entität eine andere Entität als Property enthält und
diese verschachtelte Entität weitere Entitäten enthalten kann. Dazu wird die Annotation
@Embeddedverwendet.
Zwischen zwei Ansätzen wählen
In Room gibt es zwei Möglichkeiten, eine Beziehung zwischen Entitäten zu definieren und abzufragen. Sie können Folgendes verwenden:
- Eine Zwischen-Datenklasse mit eingebetteten Objekten oder
- Eine relationale Abfragefunktion mit einem Multimap-Rückgabetyp.
Wenn Sie keinen bestimmten Grund haben, Zwischen-Datenklassen zu verwenden, empfehlen wir den Ansatz mit dem Multimap-Rückgabetyp. Weitere Informationen zu diesem Ansatz finden Sie unter Multimap zurückgeben.
Mit dem Ansatz mit der Zwischen-Datenklasse können Sie komplexe SQL-Abfragen vermeiden. Er kann aber auch die Codekomplexität erhöhen, da zusätzliche Datenklassen erforderlich sind. Kurz gesagt: Beim Ansatz mit dem Multimap-Rückgabetyp müssen Ihre SQL-Abfragen mehr Arbeit leisten, während beim Ansatz mit der Zwischen-Datenklasse mehr Arbeit im Code anfällt.
Ansatz mit der Zwischen-Datenklasse verwenden
Beim Ansatz mit der Zwischen-Datenklasse definieren Sie eine Datenklasse, die die Beziehung zwischen Ihren Room-Entitäten modelliert. Diese Datenklasse enthält die Paare zwischen Instanzen einer Entität und Instanzen einer anderen Entität als eingebettete Objekte. Ihre Abfragefunktionen können dann Instanzen dieser Datenklasse zur Verwendung in Ihrer App zurückgeben.
Sie können beispielsweise eine UserBook-Datenklasse definieren, um Bibliotheksnutzer mit bestimmten ausgeliehenen Büchern darzustellen, und eine Abfragefunktion definieren, um eine Liste von UserBook-Instanzen aus der Datenbank abzurufen:
@Dao interface UserBookDao { @Query( """ SELECT user.name AS userName, book.name AS bookName FROM user JOIN book ON user.id = book.user_id """ ) fun loadUserAndBookNames(): LiveData<List<UserBook>> } data class UserBook(val userName: String, val bookName: String)
Ansatz mit dem Multimap-Rückgabetyp verwenden
Beim Ansatz mit dem Multimap-Rückgabetyp müssen Sie keine zusätzlichen Datenklassen definieren. Stattdessen definieren Sie einen Multimap-Rückgabetyp für Ihre Funktion basierend auf der gewünschten Zuordnungsstruktur und definieren die Beziehung zwischen Ihren Entitäten direkt in Ihrer SQL-Abfrage.
Die folgende Abfragefunktion gibt beispielsweise eine Zuordnung von User- und Book-Instanzen zurück, um Bibliotheksnutzer mit bestimmten ausgeliehenen Büchern darzustellen:
@Query( """ SELECT * FROM user JOIN book ON user.id = book.user_id """ ) suspend fun loadUserAndBookNames(): Map<User, List<Book>>
Mit Multimap-Rückgabetypen können Sie auch 1:1-Beziehungen abfragen, die keine andere Entität umfassen. Die folgende Abfragefunktion gibt eine Zuordnung von
User und der Anzahl der ausgeliehenen Bücher zurück. Dazu wird die
@MapColumn Annotation verwendet:
@Query( """ SELECT user.*, COUNT(book.id) AS book_count FROM user LEFT JOIN book ON user.id = book.user_id GROUP BY user.id """ ) suspend fun loadUserAndBookCount(): Map<User, @MapColumn(columnName = "book_count") Int>
Eingebettete Objekte erstellen
Manchmal möchten Sie eine Entität oder ein Datenobjekt in Ihrer Datenbanklogik als zusammenhängendes Ganzes darstellen, auch wenn das Objekt mehrere Properties enthält. Verwenden Sie in diesen
Fällen die @Embedded Annotation, um ein Objekt in
einer Tabelle in seine Unter-Properties zu zerlegen. Sie können dann die eingebetteten Properties genauso abfragen wie andere Spalten.
Ihre User-Klasse kann beispielsweise eine Address-Property enthalten, die eine Zusammensetzung der Properties street, city, state und postCode darstellt. Wenn Sie die zusammengesetzten Spalten separat in der Tabelle speichern möchten, annotieren Sie die Address-Property in der User-Klasse mit @Embedded. Das folgende Code-Snippet zeigt diese Einrichtung:
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? )
Die Tabelle, die ein User-Objekt darstellt, enthält dann Spalten mit den folgenden Namen: id, firstName, street, state, city und post_code.
Wenn eine Entität mehrere eingebettete Properties desselben Typs hat, können Sie
jede Spalte eindeutig halten, indem Sie die prefix Property festlegen. Room fügt dann den angegebenen Wert am Anfang jedes Spaltennamens im eingebetteten Objekt hinzu.