房间@Relation与复合主键

时间:2018-05-15 12:52:42

标签: android sqlite android-room composite-primary-key

我的问题是这个问题的延伸(也是我的:)) - > Room composite Primary Key link to Foreign Key 所以,如果我有这个课程:

public class FoodWithIngredients extends Food{

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

}

但“食物”表的PrimaryKeycomposite (primaryKeys = {"id", "language_id"})

如何让@Relation返回"parentColumn = {"id", "language_id"}, entityColumn = {"food_id", food_language_id}"

的记录

3 个答案:

答案 0 :(得分:6)

注释@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?
)

然后,我们要查询所有与其中工作的人员和机器以及工作时间相关的workOrders。因此,我们在Dao中编写了一个查询。

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

在测试SQL时,映射到实体FullWorkOrder的行为就像在表可视化视图上的Db查询一样,必须进行映射,以免重复数据行或错误分配数据,具体取决于联接的复杂性。我建议将数据移至键值映射,然后加入所有togheter过滤重复的键。在这种情况下,我们将映射到我们在UI-> DomainWorkOrder上使用的实体。

data class DomainWorkOrder(
    val id: String,
    .
    .
    .
    val staffList: List<StaffRow>
    val machineList: List<MachineRow>
)

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

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

答案 1 :(得分:0)

我找到了一些解决方法。但这会影响性能。 您需要在关系字段中添加特殊的吸气剂,以使用复合主键的其他部分过滤结果。

对于您来说,它看起来像:

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

}

答案 2 :(得分:0)

Room 2.4.0-alpha04 开始,您可以编写 DAO 方法,为 1:N 关系返回 Map<Entity, List<Relation>>。这使您能够编写 JOIN 查询,指定如何获取关系,可选择指定 WHEREORDER BY 子句。

来源:https://issuetracker.google.com/issues/64247765