Wenn Sie die Room-Persistence-Bibliothek zum Speichern der Daten Ihrer Anwendung verwenden, interagieren Sie mit den gespeicherten Daten, indem Sie Data Access Objects (DAOs) definieren. Jede DAO enthält Methoden, die abstrakten Zugriff auf die Datenbank Ihrer Anwendung ermöglichen. Zur Kompilierungszeit generiert Room automatisch Implementierungen der von Ihnen definierten DAOs.
Wenn Sie DAOs für den Zugriff auf die Datenbank Ihrer Anwendung anstelle von Abfrage-Buildern oder direkten Abfragen verwenden, können Sie die Trennung von Angelegenheiten wahren, ein wichtiges Architekturprinzip. DAOs erleichtern Ihnen auch das Simulieren des Datenbankzugriffs, wenn Sie Ihre Anwendung testen.
Anatomie eines DAO
Sie können jedes DAO entweder als Schnittstelle oder als abstrakte Klasse definieren. Für grundlegende Anwendungsfälle verwenden Sie normalerweise eine Schnittstelle. In beiden Fällen müssen Sie Ihre DAOs immer mit @Dao
annotieren. DAOs haben keine Attribute, definieren aber eine oder mehrere Methoden für die Interaktion mit den Daten in der Datenbank Ihrer Anwendung.
Der folgende Code ist ein Beispiel für einen einfachen DAO, der Methoden zum Einfügen, Löschen und Auswählen von User
-Objekten in einer Raumdatenbank definiert:
Kotlin
@Dao interface UserDao { @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) @Query("SELECT * FROM user") fun getAll(): List<User> }
Java
@Dao public interface UserDao { @Insert void insertAll(User... users); @Delete void delete(User user); @Query("SELECT * FROM user") List<User> getAll(); }
Es gibt zwei Arten von DAO-Methoden, mit denen Datenbankinteraktionen definiert werden:
- Praktische Methoden, mit denen Sie Zeilen in Ihrer Datenbank einfügen, aktualisieren und löschen können, ohne SQL-Code schreiben zu müssen.
- Abfragemethoden, mit denen Sie eine eigene SQL-Abfrage schreiben können, um mit der Datenbank zu interagieren
In den folgenden Abschnitten wird gezeigt, wie Sie beide Arten von DAO-Methoden verwenden, um die Datenbankinteraktionen zu definieren, die Ihre Anwendung benötigt.
Praktische Methoden
Room bietet praktische Anmerkungen zum Definieren von Methoden, die einfache Einfügungen, Aktualisierungen und Löschungen ausführen, ohne dass Sie eine SQL-Anweisung schreiben müssen.
Wenn Sie komplexere Einfügungen, Aktualisierungen oder Löschungen definieren oder die Daten in der Datenbank abfragen müssen, verwenden Sie stattdessen eine Abfragemethode.
Einfügen
Mit der Annotation @Insert
können Sie Methoden definieren, die ihre Parameter in die entsprechende Tabelle in der Datenbank einfügen. Der folgende Code zeigt Beispiele für gültige @Insert
-Methoden, mit denen ein oder mehrere User
-Objekte in die Datenbank eingefügt werden:
Kotlin
@Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertUsers(vararg users: User) @Insert fun insertBothUsers(user1: User, user2: User) @Insert fun insertUsersAndFriends(user: User, friends: List<User>) }
Java
@Dao public interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(User... users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List<User> friends); }
Jeder Parameter für eine @Insert
-Methode muss entweder eine Instanz einer mit @Entity
annotierten Raumdatenentitätsklasse oder eine Sammlung von Instanzen einer Datenentitätsklasse sein, die jeweils auf eine Datenbank verweisen. Beim Aufruf einer @Insert
-Methode fügt Room jede übergebene Entitätsinstanz in die entsprechende Datenbanktabelle ein.
Wenn die Methode @Insert
einen einzelnen Parameter empfängt, kann sie einen long
-Wert zurückgeben. Dies ist der neue rowId
-Wert für das eingefügte Element. Wenn der Parameter ein Array oder eine Sammlung ist, wird stattdessen ein Array oder eine Sammlung von long
-Werten zurückgegeben, wobei jeder Wert für eines der eingefügten Elemente der rowId
ist. Weitere Informationen zur Rückgabe von rowId
-Werten finden Sie in der Referenzdokumentation zur Annotation @Insert
und in der SQLite-Dokumentation für rowid-Tabellen.
Aktualisieren
Mit der Annotation @Update
können Sie Methoden definieren, mit denen bestimmte Zeilen in einer Datenbanktabelle aktualisiert werden. Wie die @Insert
-Methoden akzeptieren auch @Update
-Methoden Datenentitätsinstanzen als Parameter.
Der folgende Code zeigt ein Beispiel für eine @Update
-Methode, mit der versucht wird, ein oder mehrere User
-Objekte in der Datenbank zu aktualisieren:
Kotlin
@Dao interface UserDao { @Update fun updateUsers(vararg users: User) }
Java
@Dao public interface UserDao { @Update public void updateUsers(User... users); }
Room verwendet den Primärschlüssel, um übergebene Entitätsinstanzen den Zeilen in der Datenbank zuzuordnen. Wenn keine Zeile mit demselben Primärschlüssel vorhanden ist, nimmt Room keine Änderungen vor.
Eine @Update
-Methode kann optional einen int
-Wert zurückgeben, der die Anzahl der erfolgreich aktualisierten Zeilen angibt.
Löschen
Mit der Annotation @Delete
können Sie Methoden definieren, mit denen bestimmte Zeilen aus einer Datenbanktabelle gelöscht werden. Wie die @Insert
-Methoden akzeptieren auch @Delete
-Methoden Datenentitätsinstanzen als Parameter.
Der folgende Code zeigt ein Beispiel für eine @Delete
-Methode, mit der versucht wird, ein oder mehrere User
-Objekte aus der Datenbank zu löschen:
Kotlin
@Dao interface UserDao { @Delete fun deleteUsers(vararg users: User) }
Java
@Dao public interface UserDao { @Delete public void deleteUsers(User... users); }
Room verwendet den Primärschlüssel, um übergebene Entitätsinstanzen den Zeilen in der Datenbank zuzuordnen. Wenn keine Zeile mit demselben Primärschlüssel vorhanden ist, nimmt Room keine Änderungen vor.
Die Methode @Delete
kann optional einen int
-Wert zurückgeben, der die Anzahl der erfolgreich gelöschten Zeilen angibt.
Abfragemethoden
Mit der Annotation @Query
können Sie SQL-Anweisungen schreiben und als DAO-Methoden verfügbar machen. Mit diesen Abfragemethoden können Sie Daten aus der Datenbank Ihrer Anwendung abfragen oder wenn Sie komplexere Einfügungen, Aktualisierungen und Löschungen ausführen müssen.
Room validiert SQL-Abfragen zum Zeitpunkt der Kompilierung. Wenn also mit Ihrer Abfrage ein Problem auftritt, tritt anstelle eines Laufzeitfehlers ein Kompilierungsfehler auf.
Einfache Abfragen
Der folgende Code definiert eine Methode, die mit einer einfachen SELECT
-Abfrage alle User
-Objekte in der Datenbank zurückgibt:
Kotlin
@Query("SELECT * FROM user") fun loadAllUsers(): Array<User>
Java
@Query("SELECT * FROM user") public User[] loadAllUsers();
In den folgenden Abschnitten wird gezeigt, wie Sie dieses Beispiel für typische Anwendungsfälle ändern können.
Teilmenge der Spalten einer Tabelle zurückgeben
Meistens müssen Sie nur eine Teilmenge der Spalten aus der Tabelle zurückgeben, die Sie abfragen. Ihre UI kann beispielsweise nur den Vor- und Nachnamen für einen Nutzer anstelle aller Details zu diesem Nutzer anzeigen. Fragen Sie nur die Felder ab, die Sie benötigen, um Ressourcen zu sparen und die Ausführung Ihrer Abfrage zu optimieren.
Mit Room können Sie ein einfaches Objekt aus einer Ihrer Abfragen zurückgeben, solange Sie die Ergebnisspalten dem zurückgegebenen Objekt zuordnen können. Sie können beispielsweise das folgende Objekt definieren, das den Vor- und Nachnamen eines Nutzers enthält:
Kotlin
data class NameTuple( @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
Java
public class NameTuple { @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") @NonNull public String lastName; }
Anschließend können Sie dieses einfache Objekt aus Ihrer Abfragemethode zurückgeben:
Kotlin
@Query("SELECT first_name, last_name FROM user") fun loadFullName(): List<NameTuple>
Java
@Query("SELECT first_name, last_name FROM user") public List<NameTuple> loadFullName();
Room erkennt, dass die Abfrage Werte für die Spalten first_name
und last_name
zurückgibt und dass diese Werte den Feldern in der Klasse NameTuple
zugeordnet werden können. Wenn die Abfrage eine Spalte zurückgibt, die keinem Feld im zurückgegebenen Objekt zugeordnet ist, zeigt Room eine Warnung an.
Einfache Parameter an eine Abfrage übergeben
In den meisten Fällen müssen Ihre DAO-Methoden Parameter akzeptieren, damit sie Filtervorgänge ausführen können. Room unterstützt die Verwendung von Methodenparametern als Bindungsparameter in Ihren Abfragen.
Der folgende Code definiert beispielsweise eine Methode, die alle Nutzer ab einem bestimmten Alter zurückgibt:
Kotlin
@Query("SELECT * FROM user WHERE age > :minAge") fun loadAllUsersOlderThan(minAge: Int): Array<User>
Java
@Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge);
Sie können auch mehrere Parameter übergeben oder in einer Abfrage mehrmals auf denselben Parameter verweisen, wie im folgenden Code gezeigt:
Kotlin
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User> @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") fun findUserWithName(search: String): List<User>
Java
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") public User[] loadAllUsersBetweenAges(int minAge, int maxAge); @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") public List<User> findUserWithName(String search);
Sammlung von Parametern an eine Abfrage übergeben
Bei einigen Ihrer DAO-Methoden müssen Sie möglicherweise eine variable Anzahl von Parametern übergeben, die bis zur Laufzeit nicht bekannt sind. Room erkennt, wann ein Parameter eine Sammlung darstellt, und erweitert sie zur Laufzeit automatisch auf Grundlage der Anzahl der angegebenen Parameter.
Beispielsweise definiert der folgende Code eine Methode, die Informationen zu allen Nutzern aus einer Teilmenge von Regionen zurückgibt:
Kotlin
@Query("SELECT * FROM user WHERE region IN (:regions)") fun loadUsersFromRegions(regions: List<String>): List<User>
Java
@Query("SELECT * FROM user WHERE region IN (:regions)") public List<User> loadUsersFromRegions(List<String> regions);
Mehrere Tabellen abfragen
Einige Ihrer Abfragen benötigen möglicherweise Zugriff auf mehrere Tabellen, um das Ergebnis zu berechnen. Sie können JOIN
-Klauseln in Ihren SQL-Abfragen verwenden, um auf mehr als eine Tabelle zu verweisen.
Der folgende Code definiert eine Methode, mit der drei Tabellen miteinander verknüpft werden, um die Bücher zurückzugeben, die derzeit einem bestimmten Nutzer ausgeliehen werden:
Kotlin
@Query( "SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName" ) fun findBooksBorrowedByNameSync(userName: String): List<Book>
Java
@Query("SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName") public List<Book> findBooksBorrowedByNameSync(String userName);
Sie können auch einfache Objekte definieren, um eine Teilmenge von Spalten aus mehreren verknüpften Tabellen zurückzugeben, wie im Abschnitt Eine Teilmenge der Spalten einer Tabelle zurückgeben beschrieben. Der folgende Code definiert einen DAO mit einer Methode, die die Namen von Nutzern und die Namen der von ihnen ausgeliehenen Bücher zurückgibt:
Kotlin
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>> // You can also define this class in a separate file. 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(); // You can also define this class in a separate file, as long as you add the // "public" access modifier. static class UserBook { public String userName; public String bookName; } }
Multimap zurückgeben
In Raum 2.4 und höher können Sie auch Spalten aus mehreren Tabellen abfragen, ohne eine zusätzliche Datenklasse zu definieren. Dazu schreiben Sie Abfragemethoden, die eine Multimap zurückgeben.
Betrachten Sie das Beispiel aus dem Abschnitt Mehrere Tabellen abfragen.
Anstatt eine Liste von Instanzen einer benutzerdefinierten Datenklasse zurückzugeben, die Paare von User
- und Book
-Instanzen enthält, können Sie eine Zuordnung von User
und Book
direkt über Ihre Abfragemethode zurückgeben:
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();
Wenn Ihre Abfragemethode eine Multimap zurückgibt, können Sie Abfragen schreiben, die GROUP BY
-Klauseln verwenden. Dadurch können Sie die SQL-Funktionen für erweiterte Berechnungen und Filter nutzen. Du kannst beispielsweise die Methode loadUserAndBookNames()
so ändern, dass nur Nutzer zurückgegeben werden, die drei oder mehr Bücher ausgecheckt haben:
Kotlin
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" + "GROUP BY user.name WHERE COUNT(book.id) >= 3" ) fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" + "GROUP BY user.name WHERE COUNT(book.id) >= 3" ) public Map<User, List<Book>> loadUserAndBookNames();
Wenn Sie keine gesamten Objekte zuordnen müssen, können Sie auch Zuordnungen zwischen bestimmten Spalten in Ihrer Abfrage zurückgeben. Legen Sie dazu die Attribute keyColumn
und valueColumn
in einer @MapInfo
-Annotation Ihrer Abfragemethode fest:
Kotlin
@MapInfo(keyColumn = "userName", valueColumn = "bookName") @Query( "SELECT user.name AS username, book.name AS bookname FROM user" + "JOIN book ON user.id = book.user_id" ) fun loadUserAndBookNames(): Map<String, List<String>>
Java
@MapInfo(keyColumn = "userName", valueColumn = "bookName") @Query( "SELECT user.name AS username, book.name AS bookname FROM user" + "JOIN book ON user.id = book.user_id" ) public Map<String, List<String>> loadUserAndBookNames();
Spezielle Rückgabearten
Room bietet einige spezielle Rückgabetypen für die Integration in andere API-Bibliotheken.
Abfragen mit Paginierung mit der Paging-Bibliothek
Room unterstützt paginierte Abfragen über die Einbindung in die Paging-Bibliothek. In Room 2.3.0-alpha01 und höher können DAOs PagingSource
-Objekte zur Verwendung mit Paging 3 zurückgeben.
Kotlin
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") fun pagingSource(query: String): PagingSource<Int, User> }
Java
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") PagingSource<Integer, User> pagingSource(String query); }
Weitere Informationen zur Auswahl von Typparametern für ein PagingSource
finden Sie unter Schlüssel- und Werttypen auswählen.
Direkter Cursorzugriff
Wenn die Anwendungslogik direkten Zugriff auf die Rückgabezeilen erfordert, können Sie Ihre DAO-Methoden so schreiben, dass ein Cursor
-Objekt zurückgegeben wird, wie im folgenden Beispiel gezeigt:
Kotlin
@Dao interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") fun loadRawUsersOlderThan(minAge: Int): Cursor }
Java
@Dao public interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge); }
Weitere Informationen
Weitere Informationen zum Zugriff auf Daten mit Room-DAOs finden Sie in den folgenden zusätzlichen Ressourcen: