Как фильтровать отношения «один ко многим» с Android room db

#android #android-sqlite #android-room

#Android #android-sqlite #android-room

Вопрос:

У меня есть объект User и объект Record. Я хочу получить список, в котором отображаются все пользователи и их список записей, отфильтрованный по дате записи. Тем не менее, я застрял в том, что не смог отфильтровать результат с помощью условий.

 def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
  
 @Entity(tableName = "user_table")
public class User {
    @PrimaryKey public long id;
    public String name;
}

@Entity(tableName = "record_table")
public class Record {
    @PrimaryKey @ColumnInfo(name = "record_id")public long recordId;
    @ColumnInfo(name = "user_id") public long userId;
    public String date;
    @ColumnInfo(name = "action")
    public String action;
}

public class UserWithRecords {
    @Embedded
    protected User user;
    @Relation(
            parentColumn = "id",
            entity = Record.class,
            entityColumn = "user_id"
    )
    protected List<Record> records;
}

  

И вот весь запрос, который я пробовал:

 //in Dao  
//first try:
@Transaction
@Query("SELECT * FROM user_table LEFT JOIN record_table ON id = user_id WHERE date=:date")
public LiveData<List<UserWithRecords>> getDailyRecord(String date);

//second try:
@Transaction
@Query("SELECT * FROM user_table LEFT JOIN (SELECT * FROM record_table WHERE date=:date) ON id = user_id")
public LiveData<List<UserWithRecords>> getDailyRecord(String date);

//third try:
@Transaction
@Query("SELECT * FROM user_table INNER JOIN record_table ON id = user_id WHERE date=:date")
public LiveData<List<UserWithRecords>> getDailyRecord(String date);

  
 <User>          <Record>
 id | name     record_id | user_id | action | date
------------   ----------------------------------------
 1  | Alice         1    | 1       | walk   |2020-10-05
 2  | Ben           2    | 1       | jog    |2020-10-05
 3  | Chris         3    | 2       | bike   |2020-10-05
                    4    | 1       | walk   |2020-10-14
                    5    | 2       | jog    |2020-10-14

filtering by 2020-10-05 and get results with all queries:
id | name | record_id | action | date
----------------------------------
 1 | Alice|   1       | walk   | 2020-10-05
 1 | Alice|   2       | jog    | 2020-10-05
 1 | Alice|   4       | walk   | 2020-10-14
 2 | Ben  |   3       | bike   | 2020-10-05
 2 | Ben  |   5       | jog    | 2020-10-14
 3 | Chris|------------------------------------->3rd query don't have this row 
  

как мне получить что-то подобное с этим? Возможно ли это сделать с помощью одного запроса?

 id | name | record_id | action | date
-------------------------------------------
 1 | Alice|   1       | walk   | 2020-10-05
 1 | Alice|   2       | jog    | 2020-10-05
 2 | Ben  |   3       | bike   | 2020-10-05   
  

Ответ №1:

Давайте посмотрим, как @Relation это работает. Есть два этапа:

  1. Room выполняет запрос, который вы ввели, @Query чтобы привлечь пользователей. Как обычно, этот запрос не включает никаких объединений. Этот запрос используется для получения той части данных, которые сохраняются в основной таблице ( User в вашем случае).
  2. Room выполняет еще один запрос. Для этого он просматривает @Relation параметры и понимает, к какой таблице он должен запросить следующий запрос ( Record в вашем случае) и каким должно быть условие соединения с результатом пользователя. Важно, чтобы у вас не было возможности вмешиваться в этот процесс создания запросов. Например, вы не можете установить фильтр по Record полям. Получение результата Room преобразует его в необходимый формат (заполняет список Records ).

У вас есть выбор:

  1. Чтобы изменить отношение, установите фильтры в Records таблице (но при этом вы получите плоские данные без списка).
 public class UserWithRecords {
    @Embedded
    protected Record record;
    @Relation(
            parentColumn = "user_id",
            entity = User.class,
            entityColumn = "id"
    )
    protected User user;
}
  

и изменить запрос на:

 @Transaction
@Query("SELECT * FROM record_table WHERE date=:date")
public LiveData<List<UserWithRecords>> getDailyRecord(String date);
  
  1. @Relation Чтобы вообще не использовать, напишите запрос с объединениями, подобными тому, который вы пробовали. Но затем вы должны вручную преобразовать результат в нужную форму в коде (результат цикла и формирование списка).