Когда вы используете библиотеку постоянства комнаты для хранения данных вашего приложения, вы взаимодействуете с сохраненными данными, определяя объекты доступа к данным или DAO. Каждый DAO включает методы, которые предлагают абстрактный доступ к базе данных вашего приложения. Во время компиляции Room автоматически генерирует реализации определенных вами DAO.
Используя DAO для доступа к базе данных вашего приложения вместо построителей запросов или прямых запросов, вы можете сохранить разделение задач — важнейший архитектурный принцип. DAO также упрощают имитацию доступа к базе данных при тестировании приложения .
Анатомия ДАО
Вы можете определить каждый DAO либо как интерфейс, либо как абстрактный класс. В базовых случаях использования вы обычно используете интерфейс. В любом случае вы всегда должны аннотировать свои DAO с помощью @Dao
. У DAO нет свойств, но они определяют один или несколько методов взаимодействия с данными в базе данных вашего приложения.
Следующий код является примером простого DAO, который определяет методы для вставки, удаления и выбора объектов User
в базе данных Room:
Котлин
@Dao interface UserDao { @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) @Query("SELECT * FROM user") fun getAll(): List<User> }
Ява
@Dao public interface UserDao { @Insert void insertAll(User... users); @Delete void delete(User user); @Query("SELECT * FROM user") List<User> getAll(); }
Существует два типа методов DAO, которые определяют взаимодействие с базой данных:
- Удобные методы, позволяющие вставлять, обновлять и удалять строки в базе данных без написания кода SQL.
- Методы запроса, позволяющие написать собственный SQL-запрос для взаимодействия с базой данных.
В следующих разделах показано, как использовать оба типа методов DAO для определения взаимодействий с базой данных, необходимых вашему приложению.
Удобные методы
Room предоставляет удобные аннотации для определения методов, которые выполняют простые вставки, обновления и удаления, не требуя написания инструкции SQL.
Если вам нужно определить более сложные вставки, обновления или удаления или если вам нужно запросить данные в базе данных, используйте вместо этого метод запроса .
Вставлять
Аннотация @Insert
позволяет определять методы, которые вставляют свои параметры в соответствующую таблицу базы данных. В следующем коде показаны примеры допустимых методов @Insert
, которые вставляют один или несколько объектов User
в базу данных:
Котлин
@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>) }
Ява
@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); }
Каждый параметр метода @Insert
должен быть либо экземпляром класса объекта данных Room , аннотированным @Entity
, либо коллекцией экземпляров класса объекта данных, каждый из которых указывает на базу данных. При вызове метода @Insert
Room вставляет каждый переданный экземпляр сущности в соответствующую таблицу базы данных.
Если метод @Insert
получает один параметр, он может вернуть long
значение, которое является новым rowId
для вставленного элемента. Если параметр является массивом или коллекцией, то вместо этого верните массив или коллекцию long
значений, где каждое значение является rowId
для одного из вставленных элементов. Дополнительные сведения о возврате значений rowId
см. в справочной документации по аннотации @Insert
и в документации SQLite для таблиц rowid .
Обновлять
Аннотация @Update
позволяет определить методы, которые обновляют определенные строки в таблице базы данных. Подобно методам @Insert
, методы @Update
принимают экземпляры объектов данных в качестве параметров. В следующем коде показан пример метода @Update
, который пытается обновить один или несколько объектов User
в базе данных:
Котлин
@Dao interface UserDao { @Update fun updateUsers(vararg users: User) }
Ява
@Dao public interface UserDao { @Update public void updateUsers(User... users); }
Room использует первичный ключ для сопоставления переданных экземпляров сущностей со строками в базе данных. Если нет строки с тем же первичным ключом, Room не вносит изменений.
Метод @Update
может при необходимости возвращать значение int
указывающее количество успешно обновленных строк.
Удалить
Аннотация @Delete
позволяет определить методы, удаляющие определенные строки из таблицы базы данных. Подобно методам @Insert
, методы @Delete
принимают экземпляры объектов данных в качестве параметров. В следующем коде показан пример метода @Delete
, который пытается удалить один или несколько объектов User
из базы данных:
Котлин
@Dao interface UserDao { @Delete fun deleteUsers(vararg users: User) }
Ява
@Dao public interface UserDao { @Delete public void deleteUsers(User... users); }
Room использует первичный ключ для сопоставления переданных экземпляров сущностей со строками в базе данных. Если нет строки с тем же первичным ключом, Room не вносит изменений.
Метод @Delete
может при необходимости возвращать значение int
указывающее количество успешно удаленных строк.
Методы запроса
Аннотация @Query
позволяет писать операторы SQL и предоставлять их как методы DAO. Используйте эти методы запроса для запроса данных из базы данных вашего приложения или когда вам нужно выполнить более сложные вставки, обновления и удаления.
Room проверяет SQL-запросы во время компиляции. Это означает, что если с вашим запросом возникает проблема, вместо сбоя во время выполнения возникает ошибка компиляции.
Простые запросы
Следующий код определяет метод, который использует простой запрос SELECT
для возврата всех объектов User
в базе данных:
Котлин
@Query("SELECT * FROM user") fun loadAllUsers(): Array<User>
Ява
@Query("SELECT * FROM user") public User[] loadAllUsers();
В следующих разделах показано, как изменить этот пример для типичных случаев использования.
Вернуть подмножество столбцов таблицы
В большинстве случаев вам нужно вернуть только подмножество столбцов из запрашиваемой таблицы. Например, ваш пользовательский интерфейс может отображать только имя и фамилию пользователя вместо всех подробностей об этом пользователе. Чтобы сэкономить ресурсы и оптимизировать выполнение запроса, запрашивайте только те поля, которые вам нужны.
Room позволяет вам возвращать простой объект из любого вашего запроса, если вы можете сопоставить набор столбцов результатов с возвращаемым объектом. Например, вы можете определить следующий объект для хранения имени и фамилии пользователя:
Котлин
data class NameTuple( @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
Ява
public class NameTuple { @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") @NonNull public String lastName; }
Затем вы можете вернуть этот простой объект из метода запроса:
Котлин
@Query("SELECT first_name, last_name FROM user") fun loadFullName(): List<NameTuple>
Ява
@Query("SELECT first_name, last_name FROM user") public List<NameTuple> loadFullName();
Room понимает, что запрос возвращает значения для столбцов first_name
и last_name
и что эти значения можно сопоставить с полями в классе NameTuple
. Если запрос возвращает столбец, который не сопоставляется с полем возвращаемого объекта, Room отображает предупреждение.
Передача простых параметров в запрос
В большинстве случаев вашим методам DAO необходимо принимать параметры, чтобы они могли выполнять операции фильтрации. Room поддерживает использование параметров метода в качестве параметров привязки в ваших запросах.
Например, следующий код определяет метод, который возвращает всех пользователей старше определенного возраста:
Котлин
@Query("SELECT * FROM user WHERE age > :minAge") fun loadAllUsersOlderThan(minAge: Int): Array<User>
Ява
@Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge);
Вы также можете передать несколько параметров или несколько раз ссылаться на один и тот же параметр в запросе, как показано в следующем коде:
Котлин
@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>
Ява
@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);
Передать набор параметров в запрос
Некоторые из ваших методов DAO могут потребовать от вас передачи переменного количества параметров, которое неизвестно до времени выполнения. Room понимает, когда параметр представляет коллекцию, и автоматически расширяет ее во время выполнения в зависимости от количества предоставленных параметров.
Например, следующий код определяет метод, который возвращает информацию обо всех пользователях из подмножества регионов:
Котлин
@Query("SELECT * FROM user WHERE region IN (:regions)") fun loadUsersFromRegions(regions: List<String>): List<User>
Ява
@Query("SELECT * FROM user WHERE region IN (:regions)") public List<User> loadUsersFromRegions(List<String> regions);
Запросить несколько таблиц
Для некоторых ваших запросов может потребоваться доступ к нескольким таблицам для вычисления результата. Вы можете использовать предложения JOIN
в своих запросах SQL для ссылки на более чем одну таблицу.
Следующий код определяет метод, который объединяет три таблицы для возврата книг, которые в данный момент предоставлены конкретному пользователю:
Котлин
@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>
Ява
@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);
Вы также можете определить простые объекты для возврата подмножества столбцов из нескольких объединенных таблиц, как описано в разделе «Возврат подмножества столбцов таблицы» . Следующий код определяет 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>> // You can also define this class in a separate file. data class UserBook(val userName: String?, val bookName: String?) }
Ява
@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; } }
Вернуть мультикарту
В версии 2.4 и выше вы также можете запрашивать столбцы из нескольких таблиц без определения дополнительного класса данных, написав методы запроса, которые возвращают multimap .
Рассмотрим пример из раздела «Запрос к нескольким таблицам» . Вместо возврата списка экземпляров пользовательского класса данных, содержащего пары экземпляров User
и Book
, вы можете вернуть сопоставление User
и Book
непосредственно из метода запроса:
Котлин
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) fun loadUserAndBookNames(): Map<User, List<Book>>
Ява
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) public Map<User, List<Book>> loadUserAndBookNames();
Когда ваш метод запроса возвращает мультикарту, вы можете писать запросы, использующие предложения GROUP BY
, что позволяет вам воспользоваться возможностями SQL для расширенных вычислений и фильтрации. Например, вы можете изменить метод loadUserAndBookNames()
, чтобы он возвращал только пользователей с тремя или более извлеченными книгами:
Котлин
@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>>
Ява
@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();
Если вам не нужно сопоставлять целые объекты, вы также можете вернуть сопоставления между конкретными столбцами в вашем запросе, установив атрибуты keyColumn
и valueColumn
в аннотации @MapInfo
для вашего метода запроса:
Котлин
@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>>
Ява
@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();
Специальные типы возврата
Room предоставляет некоторые специальные типы возврата для интеграции с другими библиотеками API.
Запросы с разбиением на страницы с помощью библиотеки подкачки
Room поддерживает запросы с разбиением на страницы благодаря интеграции с библиотекой подкачки . В Room 2.3.0-alpha01 и выше DAO могут возвращать объекты PagingSource
для использования с Paging 3 .
Котлин
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") fun pagingSource(query: String): PagingSource<Int, User> }
Ява
@Dao interface UserDao { @Query("SELECT * FROM users WHERE label LIKE :query") PagingSource<Integer, User> pagingSource(String query); }
Дополнительные сведения о выборе параметров типа для PagingSource
см. в разделе Выбор типов ключа и значения .
Прямой доступ к курсору
Если логика вашего приложения требует прямого доступа к возвращаемым строкам, вы можете написать методы DAO для возврата объекта Cursor
, как показано в следующем примере:
Котлин
@Dao interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") fun loadRawUsersOlderThan(minAge: Int): Cursor }
Ява
@Dao public interface UserDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge); }
Дополнительные ресурсы
Чтобы узнать больше о доступе к данным с помощью Room DAO, см. следующие дополнительные ресурсы: