如何通过子 POJO 的属性过滤复合 ManyToMany POJO?
How can I filter composite ManyToMany POJOs by children POJO's attributes?
我有两个这样的房间实体:
@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;
}
...和一个连接点 table 用于像这样的多对多关系:
@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;
}
...和一些复合 class 以获得某种“复合 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);
}
问题的重点是...
有效的 returns 列表如预期:每个 TeacherWithCourses
都有教师和 List
课程。第二个没有:生成的 TeacherWithCourses
对象正确加载了 Teacher
属性,但是 List<Courses>
属性 有一个空列表,尽管复杂的 SELECT
查询基于预期的 INNER JOINS
个过滤器。
那么,我怎样才能像第一个 DAO 方法一样获得完整的 TeacherWithCourses
对象列表,而不是按课程 ID 过滤?
我认为您的问题是由于 列名称重复 并且基本上房间选择了不正确的值(我相信它使用最后一个,所以它会使用课程 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
}
连同:-
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
}
- 注意到 teachercourses table 仅用于链接替代课程(而不是创建 altteachercourses table)
和:-
@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
因此解决方案是
- 使用唯一的列名,或者
- 使用@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);
BUT 然后你还需要使用 :-
@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);
补充评论:-
the only issue is that "ORDER BY" statement didn't seem to affect this "child list" 's sorting. But that one might be subject for a new question.
这个问题是由于@Relationship 的工作方式造成的。
@Relationship
通过底层查询获取 parent(s) 的 ALL @Relation
objects 来工作。检索 children 时不考虑 @Query 中任何不影响检索到的 parents 的内容。因此,您无法控制订单。
也许可以考虑使用 CourseWithTeachers 方法,但这样您就无法控制教师的顺序。另一种方法是对 parent 和 child 使用 @Embedded,但你必须处理结果,即笛卡尔积,即每个 parent/child 组合的结果。
我有两个这样的房间实体:
@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;
}
...和一个连接点 table 用于像这样的多对多关系:
@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;
}
...和一些复合 class 以获得某种“复合 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);
}
问题的重点是...
有效的 returns 列表如预期:每个 TeacherWithCourses
都有教师和 List
课程。第二个没有:生成的 TeacherWithCourses
对象正确加载了 Teacher
属性,但是 List<Courses>
属性 有一个空列表,尽管复杂的 SELECT
查询基于预期的 INNER JOINS
个过滤器。
那么,我怎样才能像第一个 DAO 方法一样获得完整的 TeacherWithCourses
对象列表,而不是按课程 ID 过滤?
我认为您的问题是由于 列名称重复 并且基本上房间选择了不正确的值(我相信它使用最后一个,所以它会使用课程 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
}
连同:-
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
}
- 注意到 teachercourses table 仅用于链接替代课程(而不是创建 altteachercourses table)
和:-
@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
因此解决方案是
- 使用唯一的列名,或者
- 使用@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);
BUT 然后你还需要使用 :-
@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);
补充评论:-
the only issue is that "ORDER BY" statement didn't seem to affect this "child list" 's sorting. But that one might be subject for a new question.
这个问题是由于@Relationship 的工作方式造成的。
@Relationship
通过底层查询获取 parent(s) 的 ALL @Relation
objects 来工作。检索 children 时不考虑 @Query 中任何不影响检索到的 parents 的内容。因此,您无法控制订单。
也许可以考虑使用 CourseWithTeachers 方法,但这样您就无法控制教师的顺序。另一种方法是对 parent 和 child 使用 @Embedded,但你必须处理结果,即笛卡尔积,即每个 parent/child 组合的结果。