В этом уроке поговорим подробнее про Query. В каком виде мы можем получать данные: List, массив, Cursor, LiveData. Как передавать параметры. Как получать только некоторые поля. Как с помощью Query выполнять update и delete запросы в Room.
Полный список уроков курса:
- Урок 1. Lifecycle
- Урок 2. LiveData
- Урок 3. LiveData. Дополнительные возможности
- Урок 4. ViewModel
- Урок 5. Room. Основы
- Урок 6. Room. Entity
- Урок 7. Room. Insert, Update, Delete, Transaction
- Урок 8. Room. Query
- Урок 9. Room. RxJava
- Урок 10. Room. Запрос из нескольких таблиц. Relation
- Урок 11. Room. Type converter
- Урок 12. Room. Миграция версий базы данных
- Урок 13. Room. Тестирование
- Урок 14. Paging Library. Основы
- Урок 15. Paging Library. PagedList и DataSource. Placeholders.
- Урок 16. Paging Library. LivePagedListBuilder. BoundaryCallback.
- Урок 17. Paging Library. Виды DataSource
- Урок 18. Android Data Binding. Основы
- Урок 19. Android Data Binding. Код в layout. Доступ к View
- Урок 20. Android Data Binding. Обработка событий
- Урок 21. Android Data Binding. Observable поля. Двусторонний биндинг.
- Урок 22. Android Data Binding. Adapter. Conversion.
- Урок 23. Android Data Binding. Использование с include, ViewStub и RecyclerView.
- Урок 24. Navigation Architecture Component. Введение
- Урок 25. Navigation. Передача данных. Type-safe аргументы.
- Урок 26. Navigation. Параметры навигации
- Урок 27. Navigation. NavigationUI.
- Урок 28. Navigation. Вложенный граф. Global Action. Deep Link.
- Урок 29. WorkManager. Введение
- Урок 30. WorkManager. Критерии запуска задачи.
- Урок 31. WorkManager. Последовательность выполнения задач.
- Урок 32. WorkManager. Передача и получение данных
- Урок 33. Практика. О чем это будет.
- Урок 34. Практика. TodoApp. Список задач.
- Урок 35. Практика. TodoApp. Просмотр задачи
В качестве примера будем работать с таким Entity классом:
@Entity() public class Employee { @PrimaryKey() public long id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; public int salary; }
List, массив, Cursor
Чтобы запросить из базы Employee-объекты, необходимо в Dao создать метод с аннотацией Query
@Dao public interface EmployeeDao { @Query("SELECT * FROM employee") List<Employee> getAll(); // ... }
В Query прописываем запрос, который должен вернуть данные. А в качестве возвращаемого типа указываем List<Employee>.
При вызове этого метода, Room сделает запрос в таблицу employee, конвертирует полученные данные в Employee объекты и упакует их в List.
Запрос, который вы указываете в Query проверяется на правильность синтаксиса во время компиляции. Если в нем будет ошибка, система вам сразу подскажет это.
Вместо List, мы также можем использовать массив:
@Query("SELECT * FROM employee") Employee[] getAll();
и даже Cursor, если это необходимо по каким-то причинам:
@Query("SELECT * FROM employee") Cursor getAll();
LiveData
Room умеет возвращать данные в LiveData обертке.
@Query("SELECT * FROM employee") LiveData<List<Employee>> getAll();
Получение данных в коде Activity выглядит так:
LiveData<List<Employee>> employeesLiveData = db.employeeDao().getAll(); employeesLiveData.observe(this, new Observer<List<Employee>>() { @Override public void onChanged(@Nullable List<Employee> employees) { log("onChanged " + employees); } });
Получаем LiveData и подписываемся на него.
Использование LiveData имеет огромное преимущество перед использование списка или массива. Подписавшись на LiveData, вы будете получать свежие данные при их изменении в базе. Т.е. при добавлении новых, удалении старых или обновлении текущих данных в таблице employee, Room снова выполнит ваш Query запрос, и вы получите в onChanged методе актуальные данные с учетом последних изменений. Вам больше не надо самим запрашивать эти данные каждый раз. И все это будет приходить вам в UI поток.
Передача параметров
В Query можно передавать параметры, чтобы сделать запросы более конкретными.
Например, запрос данных по id
@Query("SELECT * FROM employee WHERE id = :employeeId") Employee getById(long employeeId);
Перед параметром employeeId в запросе должно стоять двоеточие. Room возьмет значение этого параметра из метода и подставит его в запрос.
Рассмотрим еще несколько примеров:
Поиск сотрудников с зарплатой больше заданного значения
@Query("SELECT * FROM employee WHERE salary > :minSalary") List<Employee> getAllWithSalaryMoreThan(int minSalary);
Поиск сотрудников с зарплатой в заданном диапазоне
@Query("SELECT * FROM employee WHERE salary BETWEEN :minSalary AND :maxSalary") List<Employee> getAllWithSalaryBetween(int minSalary, int maxSalary);
Поиск сотрудников по имени или фамилии
@Query("SELECT * FROM employee WHERE first_name LIKE :search OR last_name LIKE :search") List<Employee> getAllWithNameLike(String search);
Поиск сотрудников по списку id.
@Query("SELECT * FROM employee WHERE id IN (:idList)") List<Employee> getByIdList(List<Long> idList);
Subsets
Часто при запросе данных нам нужно получить из таблицы не все поля, а только некоторые. Такие запросы быстрее и легче, чем тянуть все поля.
Допустим нам надо получать только имя и фамилию сотрудника. Если сделать так:
@Query("SELECT first_name, last_name FROM employee") List<Employee> getNames();
то уже при компиляции получим ошибку: The columns returned by the query does not have the fields [id,salary] in Employee even though they are annotated as non-null or primitive. Columns returned by the query: [first_name,last_name].
Room сообщает, что в данных, которые вернет этот запрос, не хватает полей, чтобы заполнить все поля объекта Employee.
В этом случае мы можем использовать отдельный объект.
public class Name { @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; }
Обратите внимание, что он не Entity. Это обычный класс. С помощью ColumnInfo мы настраиваем имена полей, чтобы они совпадали с полями таблицы.
Используем этот класс в методе запроса:
@Query("SELECT first_name, last_name FROM employee") List<Name> getNames();
Теперь все ок, и мы получим список Name объектов.
Вы также можете в этих не Entity классах использовать вложенные классы с аннотацией @Embedded. Подробно об этой аннотации мы говорили в Уроке 6.
insert, update и delete запросы
Аннотации Insert, Update и Delete позволяют нам модифицировать данные, но их возможности слишком ограниченны. Часто возникает необходимость обновить только некоторые поля или удалить записи по определенному условию. Это можно сделать запросами с помощью Query.
Давайте рассмотрим пару примеров.
Обновление зарплат у сотрудников по списку id.
@Query("UPDATE employee SET salary = :newSalary WHERE id IN (:idList)") int updateSalaryByIdList(List<Long> idList, int newSalary);
Опционально метод может возвращать int значение, в котором мы получим количество обновленных строк. Если вам это не нужно, то делайте метод void.
Вызов метода будет выглядеть так:
int updatedCount = db.employeeDao().updateSalaryByIdList(Arrays.asList(1L, 3L, 4L), 10000);
Удаление сотрудников по списку id
@Query("DELETE from employee WHERE id IN (:idList)") int deleteByIdList(List<Long> idList);
Запросы удаления также могут возвращать int значение, в котором мы получим количество удаленных строк.
Вызов метода:
int deletedCount = db.employeeDao().deleteByIdList(Arrays.asList(1L, 3L, 4L));
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня