android - POJO 中的双向关系(父 <=> 子)
问题描述
房间里的 POJO 是否可以有双向关系?
就像在我写的以下测试中一样:
data class TestChildWithRefs(
@Embedded val child: TestChild,
// REMOVING following reverse relation solves the problem; only referenceing TestParent instead of TestParentWithRefs also works but is not what I need
@Relation(parentColumn = "fk_parent", entityColumn = "parent_id", entity = TestParent::class)
val parent: TestParentWithRefs
)
data class TestParentWithRefs(
@Embedded val parent: TestParent,
@Relation(parentColumn = "parent_id", entityColumn = "fk_parent", entity = TestChild::class)
val children: MutableList<TestChildWithRefs>
)
一旦我添加了双向关系,房间就会StackOverflowError
因为无休止的递归而停止构建。我能以某种方式解决这个问题吗?
我需要这个,因为我的数据层将通过已经定义的接口公开父母和孩子,并且允许检索从父母到孩子的导航以及相反的方式......
附加代码:
// ------
// Child entity + dao
// -----
@Entity(tableName = "child")
data class TestChild(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "child_id") var id: Long = 0L,
@ColumnInfo(name = "name") var name: String,
@ColumnInfo(name = "fk_parent") var fkParent: Long
)
@Dao
abstract class TestChildDao {
@Transaction
@Query("SELECT * FROM child")
abstract fun loadAll(): List<TestChildWithRefs>
}
// ------
// Parent entity + dao
// -----
@Entity(tableName = "parent")
data class TestParent(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "parent_id") var id: Long = 0L,
@ColumnInfo(name = "name") var name: String
)
@Dao
abstract class TestParentDao {
@Transaction
@Query("SELECT * FROM parent")
abstract fun loadAll(): List<TestParentWithRefs>
}
解决方案
我找到了一种在房间中建模双向关系的方法,只需少量(但可以接受)手动交互。
这是基于我的真实解决方案的示例:
- 1 天(父母)=> 有很多锻炼
- 1 次锻炼(儿童)=> 有很多锻炼
- 1个练习(孩子的孩子)=>我会在这个级别停下来,这个例子解释了这个想法
具有工作双向关系的实体包装器
class FullDay(
@Embedded
val day: WDay,
@Relation(parentColumn = "w_day_id", entityColumn = "fk_day", entity = WWorkout::class)
val workouts: MutableList<FullWorkout> = ArrayList()
) {
fun setRefs() {
workouts.forEach { it.setRefs(this) }
}
}
class FullWorkout(
@Embedded
val workout: WWorkout,
@Relation(parentColumn = "w_workout_id", entityColumn = "fk_workout", entity = WExercise::class)
val internalExercises: MutableList<FullExercise> = ArrayList()
) {
@Ignore
var day: FullDay? = null
fun setRefs(day: FullDay) {
this.day = day
internalExercises.forEach { it.setRefs(this) }
}
}
class FullExercise(
@Embedded
val exercise: WExercise
// ...
) {
@Ignore
var workout: FullWorkout? = null
fun setRefs(fullWorkout: FullWorkout) {
this.workout = fullWorkout
}
}
工作道
@Dao
abstract class WDao : BaseDao {
companion object {
const val BASE_QUERY = "SELECT * FROM w_day " +
"left join w_workout on w_day_id = fk_day " +
"left join w_exercise on w_exercise_id = fk_exercise " +
"left join w_set on w_exercise_id = w_set_fk_exercise " +
"left join w_target_set on w_exercise_id = w_target_set_fk_exercise"
}
// ------------------
// internal functions
// ------------------
@Transaction
@Query("$BASE_QUERY where w_day_id = :id")
abstract fun _loadFullDay(id: Long): FullDay
@Transaction
@Query("$BASE_QUERY where date = :date")
abstract fun _loadFullDayOfDate(date: Date): FullDay?
@Transaction
@Query("$BASE_QUERY where w_workout_id = :id")
abstract fun _loadFullWorkout(id: Long): FullDay
@Transaction
@Query("$BASE_QUERY where date >= :date")
abstract fun _loadFullWorkoutAfterDate(date: Date): List<FullDay>
@Transaction
@Query("$BASE_QUERY where w_exercise_id = :id")
abstract fun _loadFullExercise(id: Long): FullDay
// ------------------
// external functions
// ------------------
fun loadFullDay(id: Long): FullDay = _loadFullDay(id).apply { setRefs() }
fun loadFullDayOfDate(date: Date): FullDay? = _loadFullDayOfDate(date)?.apply { setRefs() }
fun loadFullWorkout(id: Long): FullWorkout = _loadFullWorkout(id).apply { setRefs() }.workouts.first()
fun loadFullWorkoutAfterDate(date: Date): List<FullWorkout> = _loadFullWorkoutAfterDate(date).onEach { it.setRefs() }.map { it.workouts }.flatten()
fun loadFullExercise(id: Long): FullExercise = _loadFullExercise(id).apply { setRefs() }.workouts.first().internalExercises.first()
}
推荐阅读
- angular - OBJLoader: Unexpected line: "" 三全在 Azure 上不工作在本地工作
- react-native - 如何使用wix反应本机导航隐藏ios上的后退按钮
- excel - 从使用 VBA 链接文件的网站路径保存文件
- powerbi - Power Bi Desktop - 如何在表之间添加值?
- python - SQLAlchemy:动态传递模式和表名,避免 SQL 注入
- scala - 广播 TypeSafe Config 抛出异常用户类抛出异常:java.io.UTFDataFormatException:编码字符串太长:70601 字节?
- react-native - 将从一个屏幕分配的变量传递到另一个屏幕
- python-3.x - RuntimeError:张量 a (133) 的大小必须与非单维 1 的张量 b (10) 的大小相匹配
- java - 通过 IntelliJ IDEA 从 Windows 桌面远程调试在 Linux VM 上运行的 SOA 应用程序的最有效/自动化的方法是什么?
- c# - 复选框和 API 23 的问题