Da SQLite eine relationale Datenbank ist, können Sie Beziehungen zwischen Entitäten definieren. Während die meisten Bibliotheken für objektrelationale Kartierung jedoch zulassen, dass sich Entitätsobjekte gegenseitig referenzieren, ist dies in Room ausdrücklich untersagt. Weitere Informationen zu den technischen Gründen für diese Entscheidung finden Sie unter Warum sind in Room keine Objektverweise zulässig?
Arten von Beziehungen
Für „Raum“ werden die folgenden Beziehungstypen unterstützt:
- Eins-zu-eins-Beziehung: Stellt eine Beziehung dar, bei der eine einzelne Entität mit einer anderen einzelnen Entität in Beziehung steht.
- 1:n: Stellt eine Beziehung dar, bei der eine einzelne Entität mit mehreren Entitäten eines anderen Typs in Beziehung stehen kann.
- Mehrfach-zu-vielen: Stellt eine Beziehung dar, bei der mehrere Entitäten eines Typs mit mehreren Entitäten eines anderen Typs in Beziehung stehen können. Dazu ist in der Regel eine Verbindungstabelle erforderlich.
- Verschachtelte Beziehungen (mit eingebetteten Objekten): Stellt eine Beziehung dar, bei der eine Entität eine andere Entität als Feld enthält. Diese verschachtelte Entität kann wiederum andere Entitäten enthalten. Dazu wird die Annotation
@Embedded
verwendet.
Zwischen zwei Ansätzen wählen
In Room gibt es zwei Möglichkeiten, eine Beziehung zwischen Entitäten zu definieren und abzufragen. Sie haben folgende Möglichkeiten:
- Eine Zwischendatenklasse mit eingebetteten Objekten oder
- Eine relationale Abfragemethode mit einem Multimap-Rückgabetyp.
Wenn Sie keinen bestimmten Grund haben, Zwischendatenklassen zu verwenden, empfehlen wir den Ansatz mit dem Rückgabetyp „Multimap“. Weitere Informationen zu diesem Ansatz finden Sie unter Eine Multimap zurückgeben.
Mit dem Ansatz der Zwischendatenklasse können Sie das Schreiben komplexer SQL-Abfragen vermeiden. Dies kann jedoch auch zu einer erhöhten Codekomplexität führen, da zusätzliche Datenklassen erforderlich sind. Kurz gesagt: Der Ansatz mit dem Rückgabetyp „Multimap“ erfordert mehr Arbeit von Ihren SQL-Abfragen und der Ansatz mit der Zwischendatenklasse erfordert mehr Arbeit von Ihrem Code.
Zwischendatenklasse verwenden
Beim Ansatz der Zwischendatenklasse definieren Sie eine Datenklasse, die die Beziehung zwischen Ihren Raumentitäten modelliert. Diese Datenklasse enthält die Paarungen zwischen Instanzen einer Entität und Instanzen einer anderen Entität als eingebettete Objekte. Ihre Abfragemethoden können dann Instanzen dieser Datenklasse für die Verwendung in Ihrer App zurückgeben.
Sie können beispielsweise eine UserBook
-Datenklasse definieren, um Bibliotheksnutzer mit bestimmten ausgeliehenen Büchern darzustellen, und eine Abfragemethode definieren, um eine Liste der UserBook
-Instanzen aus der Datenbank abzurufen:
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;
}
Mehrere Rückgabetypen für Multimaps verwenden
Bei der Rückgabe eines Multimaps müssen Sie keine zusätzlichen Datenklassen definieren. Stattdessen definieren Sie einen Multimap-Rückgabetyp für Ihre Methode basierend auf der gewünschten Map-Struktur und definieren die Beziehung zwischen Ihren Entitäten direkt in Ihrer SQL-Abfrage.
Die folgende Abfragemethode gibt beispielsweise eine Zuordnung von User
- und Book
-Instanzen zurück, um Bibliotheksnutzer mit bestimmten ausgeliehenen Büchern darzustellen:
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();
Eingebettete Objekte erstellen
Manchmal möchten Sie eine Entität oder ein Datenobjekt in Ihrer Datenbanklogik als zusammenhängendes Ganzes ausdrücken, auch wenn das Objekt mehrere Felder enthält. In diesen Fällen können Sie die Anmerkung @Embedded
verwenden, um ein Objekt darzustellen, das Sie in einer Tabelle in seine untergeordneten Felder zerlegen möchten. Sie können die eingebetteten Felder dann wie bei anderen einzelnen Spalten abfragen.
Die User
-Klasse kann beispielsweise ein Feld vom Typ Address
enthalten, das eine Zusammensetzung der Felder street
, city
, state
und postCode
darstellt. Wenn Sie die zusammengesetzten Spalten separat in der Tabelle speichern möchten, fügen Sie ein Address
-Feld ein. Dieser sollte in der User
-Klasse mit @Embedded
gekennzeichnet sein. Das folgende Code-Snippet veranschaulicht dies:
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 den folgenden Namen: id
, firstName
, street
, state
, city
und post_code
.
Wenn eine Entität mehrere eingebettete Felder desselben Typs hat, können Sie jede Spalte eindeutig machen, indem Sie die Eigenschaft prefix
festlegen. Room fügt dann den angegebenen Wert an den Anfang jedes Spaltennamens im eingebetteten Objekt ein.
Weitere Informationen
Weitere Informationen zum Definieren von Beziehungen zwischen Entitäten in Room finden Sie in den folgenden zusätzlichen Ressourcen.
Videos
- What's New in Room (Android Dev Summit '19)