【问题标题】:How can I filter composite ManyToMany POJOs by children POJO's attributes?如何按子 POJO 的属性过滤复合多对多 POJO?
【发布时间】:2021-12-22 16:52:03
【问题描述】:

我有两个像这样的房间实体:

@Entity
public class Teacher implements Serializable {
    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "name")
    public String name;
}

@Entity
public class Course implements Serializable {
    @PrimaryKey(autoGenerate = true)
    public short id;

    @ColumnInfo(name = "name")
    public String name;
}

...还有一个像这样的多对多关系的联结表:

@Entity(primaryKeys = {"teacher_id", "course_id"})
public class TeachersCourses implements Serializable {
    @ColumnInfo(name = "teacher_id")
    public int teacherId;

    @ColumnInfo(name = "course_id")
    public short courseId;

    @ColumnInfo(index = true, name = "course_order")
    public short courseOrder;
}

...以及一些用于获取某种“复合 POJO”的复合类:

public class TeacherWithCourses implements Serializable {
    @Embedded public Teacher teacher;
    @Relation(
            parentColumn = "id",
            entity = Course.class,
            entityColumn = "id",
            associateBy = @Junction(
                    value = TeachersCourses.class,
                    parentColumn = "teacher_id",
                    entityColumn = "course_id"
            )
    )
    public List<Courses> courses;
}

...那么,我有这种“复合DAO”:

@Dao
public abstract class TeacherWithCoursesDao {
    [...]

    // XXX This one works as expected
    @Transaction
    @Query("SELECT * FROM teacher " +
           "WHERE id=:teacher_id"
    )
    public abstract LiveData<List<TeacherWithCourses>> getTeachersByTeacherId(int teacher_id);

    // XXX FIXME
    // This one succeeds at loading "parents", but each "parent"'s list of "children" is empty
    @Transaction
    @Query("SELECT * FROM teacher " +
            "INNER JOIN teacherscourses AS tc ON teacher.id = tc.teacher_id " +
            "INNER JOIN course AS c ON c.id = tc.course_id " +
            "WHERE tc.course_id = :course_id " +
            "ORDER BY teacher.id ASC, tc.course_order ASC"
    )
    public abstract LiveData<List<TeacherWithCourses>> getTeachersByCourseId(short course_id);
}

问题的重点是……

有效的会按预期返回列表:每个TeacherWithCourses 都有教师和List 的课程。第二个没有:生成的TeacherWithCourses 对象的Teacher 属性正确加载,但List&lt;Courses&gt; 属性有一个空列表,尽管基于INNER JOINS 的复杂SELECT 查询按预期过滤。

那么,我怎样才能获得完整的TeacherWithCourses 对象列表,就像在第一个 DAO 方法中一样,而是按课程 ID 进行过滤?

【问题讨论】:

    标签: java android sql many-to-many android-room


    【解决方案1】:

    我认为您的问题是由于 列名重复 并且基本上房间选择了不正确的值(我相信它使用最后一个,因此它将使用教师的课程 ID 列值id)。

    即查询(带有 JOINS)将由列组成:-

    • id(老师),
    • 姓名(老师),
    • teacher_id,
    • course_id,
    • id(课程),
    • 名称(课程)

    假设您在数据库中有以下内容:-

    并使用以下内容(LiveData 不用于简洁和方便):-

        for(Course c: dao.getAllCourses()) {
            for (TeacherWithCourses tbc: dao.getTeachersByCourseId(c.id)) {
                Log.d("TEACHER","Teacher is " + tbc.teacher.name + " Courses = " + tbc.courses.size());
                for(Course course: tbc.courses) {
                    Log.d("COURSE","\tCourse is " + course.name);
                }
            }
        }
    

    那么结果,正如你所报告的那样:-

    2021-11-10 15:25:30.994 D/TEACHER: Teacher is Course1 Courses = 0
    2021-11-10 15:25:30.996 D/TEACHER: Teacher is Course2 Courses = 0
    2021-11-10 15:25:30.999 D/TEACHER: Teacher is Course3 Courses = 0
    2021-11-10 15:25:30.999 D/TEACHER: Teacher is Course3 Courses = 0
    

    但是(修复)

    如果您使用不同的列名,例如:-

    @Entity
    public class AltCourse implements Serializable {
        @PrimaryKey(autoGenerate = true)
        public short courseid; //<<<<<<<<<<
    
        @ColumnInfo(name = "coursename") //<<<<<<<<<<
        public String coursename; //<<<<<<<<<< doesn't matter
    
    }
    
    • 添加的数据基本上复制了原始课程(相同的 id #'s),所以:-

    还有:-

    public class AltTeacherWithCourses implements Serializable {
        @Embedded
        public Teacher teacher;
        @Relation(
                parentColumn = "id",
                entity = AltCourse.class, //<<<<<<<<<< just to use alternative class
                entityColumn = "courseid", //<<<<<<<<<<
                associateBy = @Junction(
                        value = TeachersCourses.class,
                        parentColumn = "teacher_id",
                        entityColumn = "course_id"
                )
        )
        public List<AltCourse> courses; //<<<<<<<<<< just to use alternative class
    }
    
    • 请注意,使用教师课程表只是为了链接替代课程(而不是创建 altteachercourses 表)

    和:-

    @Transaction
    @Query("SELECT * FROM teacher " +
            "INNER JOIN teacherscourses AS tc ON teacher.id = tc.teacher_id " +
            "INNER JOIN altcourse AS c ON c.courseid = tc.course_id " +
            "WHERE tc.course_id = :course_id " +
            "ORDER BY teacher.id ASC, tc.course_order ASC"
    )
    public abstract List<AltTeacherWithCourses> getAltTeachersByCourseId(short course_id);
    

    然后:-

        for(Course c: dao.getAllCourses()) {
            for (AltTeacherWithCourses tbc: dao.getAltTeachersByCourseId(c.id)) {
                Log.d("TEACHER","Teacher is " + tbc.teacher.name + " Courses = " + tbc.courses.size());
                for(AltCourse course: tbc.courses) {
                    Log.d("COURSE","\tCourse is " + course.coursename);
                }
            }
        }
    

    即而不是 Course,AltCourse 用于其他相同的,那么结果是:-

    2021-11-10 15:41:09.223 D/TEACHER: Teacher is Teacher1 Courses = 3
    2021-11-10 15:41:09.223 D/COURSE:   Course is AltCourse1
    2021-11-10 15:41:09.223 D/COURSE:   Course is AltCourse2
    2021-11-10 15:41:09.223 D/COURSE:   Course is AltCourse3
    2021-11-10 15:41:09.225 D/TEACHER: Teacher is Teacher1 Courses = 3
    2021-11-10 15:41:09.225 D/COURSE:   Course is AltCourse1
    2021-11-10 15:41:09.225 D/COURSE:   Course is AltCourse2
    2021-11-10 15:41:09.225 D/COURSE:   Course is AltCourse3
    2021-11-10 15:41:09.229 D/TEACHER: Teacher is Teacher1 Courses = 3
    2021-11-10 15:41:09.229 D/COURSE:   Course is AltCourse1
    2021-11-10 15:41:09.229 D/COURSE:   Course is AltCourse2
    2021-11-10 15:41:09.229 D/COURSE:   Course is AltCourse3
    2021-11-10 15:41:09.230 D/TEACHER: Teacher is Teacher2 Courses = 1
    2021-11-10 15:41:09.230 D/COURSE:   Course is AltCourse3
    

    因此解决方案是

    1. 使用唯一的列名,或者
    2. 使用@Prefix 注释(@Embedded 的参数),例如你可以有

    :-

    public class TeacherWithCourses implements Serializable {
        @Embedded(prefix = "prefix_teacher_") //<<<<<<<<<<
        public Teacher teacher;
        @Relation(
                parentColumn = "prefix_teacher_id", //<<<<<<<<<<
                entity = Course.class,
                entityColumn = "id",
                associateBy = @Junction(
                        value = TeachersCourses.class,
                        parentColumn = "teacher_id",
                        entityColumn = "course_id"
                )
        )
        public List<Course> courses;
    }
    

    并使用:-

    @Transaction
    @Query("SELECT teacher.id AS prefix_teacher_id, teacher.name AS prefix_teacher_name, c.* FROM teacher " +
            "INNER JOIN teacherscourses AS tc ON teacher.id = tc.teacher_id " +
            "INNER JOIN course AS c ON c.id = tc.course_id " +
            "WHERE tc.course_id = :course_id " +
            "ORDER BY teacher.id ASC, tc.course_order ASC"
    )
    public abstract List<TeacherWithCourses> getTeachersByCourseId(short course_id);
    

    但是你还需要使用:-

    @Transaction
    @Query("SELECT id AS prefix_teacher_id, name as prefix_teacher_name FROM teacher " +
            "WHERE id=:teacher_id"
    )
    public abstract List<TeacherWithCourses> getTeachersByTeacherId(int teacher_id);
    

    补充评论:-

    唯一的问题是“ORDER BY”语句似乎没有影响这个“子列表”的排序。但那可能是一个新问题的主题。

    问题在于@Relationship 的工作方式。

    @Relationship 通过底层查询获取 ALL 父级的 @Relation 对象。 @Query 中不影响检索到的父级的任何内容在检索子级时都不会被考虑。因此,您无法控制订单。

    也许考虑使用 CourseWithTeachers 方法,但是您无法控制教师的顺序。另一种方法是对父母和孩子使用@Embedded,但是您必须处理结果,即笛卡尔乘积,即每个父母/孩子组合的结果。

    【讨论】:

    • 非常感谢。 “重复的 ID 名称”是问题所在。所以,看起来 Room 强制执行了这种冗余。我记下了。 (PD 我记得你回答了another of my last questions 那是关于 One2Many 关系)
    • 它有效; POJO 中的“子列表”不再为空;唯一的问题是“ORDER BY”语句似乎没有影响这个“子列表”的排序。但那可能是一个新问题的主题。
    • @SebasSBM 在答案中添加了一些内容,解释了 ORDER 问题的原因。
    • T.谢谢。我添加了关于订单的问题here
    猜你喜欢
    • 2018-10-08
    • 1970-01-01
    • 2023-01-14
    • 2015-08-18
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多