如何过滤 Room 中的嵌套关​​系?

How to filter a nested relation in Room?

让我们举个例子:我有一个表格,它有几个部分,每个部分都有问题。侧面,我有映射到问题的答案,他们有另一列我想在查询时过滤:

所以我有以下实体:

@Entity(tableName = "sections")
public class Section {
    @PrimaryKey
    public long id;
    public String title;
}
@Entity(tableName = "questions")
public class Question {
    @PrimaryKey
    public long id;
    public String title;
    public long sectionId;
}
@Entity(tableName = "answers")
public class Answer {
    @PrimaryKey
    public long id;
    public long questionId;
    public int otherColumn;
}

在 DAO 部分我想检索所有这些。

这是我希望通过此查询填充的 POJO:

class SectionWithQuestions {
    @Embedded
    public Section section;

    @Relation(parentColumn = "id", entityColumn = "sectionId", entity = Question.class)
    public List<QuestionWithAnswer> questions;

    public static class QuestionWithAnswer {
        @Embedded
        public Question question;

        @Relation(parentColumn = "id", entityColumn = "questionId", entity = Answer.class)
        List<Answer> answers;
    }
}

在另一个应用程序中,查询将是:

SELECT s.*, q.*, a.*
FROM sections s
LEFT JOIN questions q ON q.sectionId = s.id
LEFT JOIN answers a ON a.questionId = q.id
WHERE s.id = :sectionId and a.otherColumn = :otherColumn

但是在 Room 中,我发现如果你想要一个对象及其关系(如示例中的用户及其宠物),你只 select 对象,并且关系在第二个查询。那将是:

@Query("SELECT * FROM sections WHERE id = :sectionId")

那么在生成的代码中会有(伪代码):

sql = "SELECT * FROM sections WHERE id = :sectionId" // what's inside @Query
cursor = query(sql)
int indexColumn1 = cursor.getColumnIndex(col1)
int indexColumn2
... etc
while (cursor.moveToNext) {
    masterObject = new object()
    masterObject.property1 = cursor.get(indexColumn1)
    ... etc

    __fetchRelationshipXXXAsYYY(masterObject.relations) // fetch the child objects
}

而这个__fetch XXX as YYY方法如下:

sql = "SELECT field1, field2, ... FROM a WHERE foreignId IN (...)"
similar algo as previously: fetch column indices, and loop through the cursor

所以基本上它创建了 2 个查询:一个用于主对象,一个用于关系。第二个查询是自动创建的,我们无法控制它。

回到我的问题,我想要关系但也过滤子列,我被卡住了:

这在 Room 中可行吗?还是我必须自己进行子查询?

额外的问题:为什么他们不在单个查询中连接表而是创建 2 个查询?这是出于性能原因吗?


编辑以阐明我的预期:

这就是我想要写的:

@Query("SELECT s.*, q.*, a.* " +
       "FROM sections s " +
       "LEFT JOIN questions q ON q.sectionId = s.id " +
       "LEFT JOIN answers a ON a.questionId = q.id " +
       "WHERE s.id = :sectionId and a.otherColumn = :additionalIntegerFilter")
SectionWithQuestionsAndAnswers fetchFullSectionData(long sectionId);

static class SectionWithQuestionsAndAnswers {
    @Embedded Section section;
    @Relation(parentColumn = "id", entityColumn = "sectionId", entity = Question.class)
    List<QuestionWithAnswers> questions;
}
static class QuestionWithAnswers {
    @Embedded Question question;
    @Relation(parentColumn = "id", entityColumn = "questionId", entity = Answer.class)
    Answer answer; // I already know that @Relation expects List<> or Set<> which is
                   // not useful if I know I have zero or one relation (ensured
                   // through unique keys)
}

这是我想象的由 Room 实现的伪代码作为生成的代码:

function fetchFullSectionData(long sectionId, long additionalIntegerFilter) {
    query = prepare(sql); // from @Query
    query.bindLong("sectionId", sectionId);
    query.bindLong("additionalIntegerFilter", additionalIntegerFilter);
    cursor = query.execute();
    Section section = null;
    long prevQuestionId = 0;
    Question question = null;
    while (cursor.hasNext()) {
        if (section == null) {
            section = new Section();
            section.questions = new ArrayList<>();
            section.field1 = cursor.get(col1); // etc for all fields
        }
        if (prevQuestionId != cursor.get(questionIdColId)) {
            if (question != null) {
                section.questions.add(question);
            }
            question = new Question();
            question.fiedl1 = cursor.get(col1); // etc for all fields
            prevQuestionId = question.id;
        }
        if (cursor.get(answerIdColId) != null) { // has answer
            Answer answer = new Answer();
            answer.field1 = cursor.get(col1); // etc for all fields
            question.answer = answer;
        }
    }
    if (section !=null && question != null) {
        section.questions.add(question);
    }
    return section;
}

这是一个查询,我的所有对象都已提取。

我发现 Room Relations 很难处理,不是很灵活,而且很多工作都是在幕后完成的,而且很难确定具体是怎么做的。

在我的项目中,大部分时间我只创建演示对象 - 专用于某些 UI 可以用自定义 select.

填充的演示的对象

这样我就可以更好地控制我想从数据库中获取的内容(即我真正需要的内容),然后将其填充到该自定义表示对象中。

我只是粘贴我发布的功能请求中提供的信息(请参阅我对我的问题的评论):

Hi there - we have recently released a new feature where relational query methods can be defined with Multimap return types. With this new feature, you should be able to achieve the results discussed in this thread. For more info on this new feature, you can check out the following resources:

我知道 link-only 答案不是很好,但我没有机会对此进行测试。如果有人有更好的答案,我会采纳。