房间@Relation与复合主键

MrV*_*lev 11 sqlite android composite-primary-key android-room

我的问题是这个问题的扩展(也是我的:)) - > 房间复合主键到外键 所以,如果我有这个类:

public class FoodWithIngredients extends Food{

    @Relation(parentColumn = "id", entityColumn = "food_id", entity = 
    Ingredient.class)
    private List<Ingredient> mIngredients;

}
Run Code Online (Sandbox Code Playgroud)

但是PrimaryKey"食物"表是composite (primaryKeys = {"id", "language_id"}).

我如何让@Relation返回记录在哪里"parentColumn = {"id", "language_id"}, entityColumn = {"food_id", food_language_id}"

mar*_* E. 9

注释@Relation 仍然不提供对复合主键的支持。

获取数据查询多个表以保持表干净的最简单方法是使用 @Embedded 注释。如果您不介意弄脏,您可以添加一个额外的字段,您可以在其中连接主键的字段,并在该字段上使用 @Relation,这会带来维护字段的所有风险以及对其数据的潜在错误比较。可能是值得的,不知道对我来说是个坏主意。

所以干净的解决方案。提供了下一个表。

//Multiple Staff and Machine can participate on a WorkOrder and they do hours of work related to it

@Entity
data class Staff(
        @PrimaryKey val jdeNumber: String,
        val idNfc: String,
        val staffDescription: String,
        val triadorNumber: String,
        val approverId: Int)

@Entity(primaryKeys = ["machineId"])
data class Machine(
        val machineId: String,
        val machineNumber: String,
        val machineDescription: String,
        val machineNumberAux: String,
        val manufacturer: String,
        val model: String,
        val productionNumber: String,
        val hasHours: Boolean)

//A WorkOrder may have staff, machine or both
@Entity
data class WorkOrder(
        @PrimaryKey val woId: String,
        val date: Long,
        val comments: String = "",
        val userId: String,
        val partStatus: Int
)

//Embedded annotation creates all the fields from the entity inside these tables and add to the field name a prefix, then when we join tables we have no name conflict
@Entity(
        primaryKeys = ["woIdStaff", "wo_jdeNumber"],
        foreignKeys = [
                ForeignKey(entity = WorkOrder::class,
                        parentColumns = ["woId"],
                        childColumns = ["woIdStaff"],
                        onUpdate = ForeignKey.CASCADE,
                        onDelete = ForeignKey.RESTRICT)]
)
data class WorkOrderStaff(
        val woIdStaff: String,
        @Embedded(prefix = "wo_")
        val staff: Staff,
        val hourFrom: Long,
        val hourTo: Long,
        val hoursUsed: Long
)

@Entity(
        primaryKeys = ["woIdMachine", "wo_machineId"],
        foreignKeys = [
                ForeignKey(entity = WorkOrder::class,
                        parentColumns = ["woId"],
                        childColumns = ["woIdMachine"],
                        onUpdate = ForeignKey.CASCADE,
                        onDelete = ForeignKey.RESTRICT)]
)
data class WorkOrderMachine(
        val woIdMachine: String,
        @Embedded(prefix = "wo_")
        val machine: Machine,
        val hourFromMachine: Long,
        val hourToMachine: Long,
        val hoursUsedMachine: Long
)

//Important this entity is the one that maps from JOIN queries
data class FullWorkOrder(
        @Embedded
        val workOrder: WorkOrder
        @Embedded
        val staff: WorkOrderStaff?
        @Embedded
        val machine: WorkOrderMachine?
)
Run Code Online (Sandbox Code Playgroud)

然后我们要查询所有工作订单与在其中工作的员工和机器以及每个工作的小时数。所以我们在我们的 Dao 中写了一个查询。

@Query("select * from WorkOrder LEFT JOIN WorkOrderStaff ON woId = woIdStaff LEFT JOIN WorkOrderMachine ON woId = woIdMachine")
abstract fun getAllFullWorkOrders(): List<FullWorkOrder>
Run Code Online (Sandbox Code Playgroud)

当您测试 SQL 时,这种到实体 FullWorkOrder 的映射的行为类似于表可视化上的 Db 查询,您必须映射它,以免复制数据行或分配不正确的数据,具体取决于连接的复杂性。我建议将数据移动到键值映射,然后加入所有过滤重复键的集合。在这种情况下,我们将映射到我们在 UI -> DomainWorkOrder 上使用的实体。

data class DomainWorkOrder(
    val id: String,
    .
    .
    .
    val staffList: List<StaffRow>
    val machineList: List<MachineRow>
)
Run Code Online (Sandbox Code Playgroud)

我已经从示例中删除了我正在使用的表的真正复杂性,这就是为什么您在 SQL 上看不到任何复合 LEFT JOIN 的原因。我有 8 个表与 WorkOrder(1-n) 相关,其中 2 个嵌套在它们内部的 1-n 关系中。我保证这在大多数情况下都可以解决问题,如果您尝试将 Staff 表加入实体 FullWorkOrder 以获取最新数据,请小心,我对此有不好的经验。

我知道它不是纯粹的,但架构受到尊重,查询/映射过程不需要大量的工作和维护。希望能帮助到你!!


LIF*_*FED 4

我找到了一些解决方法。但我认为这会影响性能。您需要向关系字段添加特殊的 getter,它将使用复合主键的其他部分过滤结果。

在你的情况下,它看起来像:

public class FoodWithIngredients {

    @Embedded
    private Food food;

    @Relation(parentColumn = "id", entityColumn = "food_id", entity = 
    Ingredient.class)
    private List<Ingredient> mIngredients;

    public List<Ingredient> getIngredients() {
        List<Ingredient> result = List<Ingredient>();

        for (ingredient in mIngredients) {
            if (ingredient.foodLanguageId == food.languageId) {
                result.add(ingredient);   
            }
        }

        return result;
    }

}
Run Code Online (Sandbox Code Playgroud)