Room. Query
У цьому уроці поговоримо докладніше про Query. У якому вигляді ми можемо отримувати дані: List, масив, Cursor, LiveData. Як передавати параметри. Як отримувати тільки деякі поля. Як за допомогою Query виконувати update і delete запити в Room.
Як приклад будемо працювати з таким 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
Під час виклику цього методу Room зробить запит у таблицю employee, конвертує отримані дані в Employee об'єкти й упакує їх у List.
Запит, який ви вказуєте в Query, перевіряється на правильність синтаксису під час компіляції. Якщо в ньому буде помилка, система вам одразу підкаже це.
Замість List, ми також можемо використовувати масив:
@Query("SELECT * FROM employee")
Employee[] getAll();
і навіть Cursor, якщо це необхідно з якихось причин:
@Query("SELECT * FROM employee")
Cursor getAll();
LiveDataRoom вміє повертати дані в 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. Стовпці, повернуті запитом: 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.
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));