如何通過子 POJO 的屬性過濾複合多對多 POJO?

賽巴斯SBM

我有兩個這樣的房間實體:

@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<Courses>屬性有一個空列表,儘管複雜的SELECT查詢基於INNER JOINS過濾器如預期。

那麼,我怎樣才能TeacherWithCourses像在第一個 DAO 方法中那樣獲取完整對象的列表,但通過課程 ID 進行過濾呢?

邁克

我相信您的問題是由於列名被重複並且基本上房間選擇了不正確的值(我相信它使用最後一個所以它將使用課程 ID 列值作為教師 ID)。

那就是查詢(帶有 JOINS)將由列組成:-

  • 身份證(老師),
  • 姓名(老師),
  • 老師_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
}
  • noting that the teachercourses table is used just that the alternative Courses are linked (rather than create an altteachercourses table)

and :-

@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);

and then :-

    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);
            }
        }
    }

i.e. instead of Course, AltCourse is used in an otherwise an identical, then the result is :-

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

As such the solution is to either

  1. use unique column names, or
  2. use the @Prefix annotation (parameter of the @Embedded) e.g. you could have

:-

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;
}

and use :-

@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 you would then also need to use :-

@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);

Additional re comment :-

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.

The issue is due to how @Relationship works.

@Relationship通過獲取工作全部@Relation通過基礎查詢父(S)的對象。檢索子項時不考慮@Query 中不影響檢索到的父項的任何內容。因此,您無法控制 ORDER。

也許考慮 CourseWithTeachers 方法,但是您無法控制教師的訂單。另一種方法是將@Embedded 用於父級和子級,但是您必須處理結果,即角叉乘積,即每個父/子組合的結果。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章