Android Room中交叉引用表的ForeignKey有何作用?
我在应用里定义了CardEntity和TagEntity两个实体,二者是多对多关系,用下面的交叉引用表建模:
@Entity( primaryKeys = ["card_id", "tag_id"], foreignKeys = [ ForeignKey( entity = CardEntity::class, parentColumns = ["card_id"],// id in CardEntity childColumns = ["card_id"],// id in TagCardCrossRefEntity onDelete = ForeignKey.CASCADE, ), ForeignKey( entity = TagEntity::class, parentColumns = ["tag_id"],// id in TagEntity childColumns = ["tag_id"],// id in TagCardCrossRefEntity onDelete = ForeignKey.CASCADE, ) ] ) data class TagCardCrossRefEntity( @ColumnInfo(name = "card_id") val cardId: Long, @ColumnInfo(name = "tag_id") val tagId: Long )
我已经实现了所有必要的操作方法,测试都通过,能正常插入、查询和删除带标签的卡片。原本以为ForeignKey的作用之一是定义onDelete级联策略,但我移除ForeignKey声明后,删除CardEntity时对应的关系记录依然会被删除。所以我疑惑:为什么要在交叉引用表中定义ForeignKey?
核心原因有这几点:
强制数据完整性
没有ForeignKey约束时,你能插入不存在的card_id或tag_id到交叉表,这会产生无效的关联数据。ForeignKey会直接拦截这种非法插入,保证交叉表里的每一条记录都对应真实存在的卡片和标签,从根源上避免脏数据。级联操作的可靠性保障
你现在删除卡片时关系记录被删除,大概率是因为DAO层手动写了删除交叉表记录的逻辑。但如果依赖ForeignKey的CASCADE策略,数据库层面会自动处理级联删除/更新,不用你在业务代码里手动维护,既能减少代码量,也能避免业务逻辑遗漏导致的数据不一致(比如忘了删交叉表记录的情况)。数据库层面的约束一致性
要是其他地方直接操作数据库(比如调试工具、第三方插件),没有ForeignKey的话,删除卡片后交叉表的无效记录会残留,导致数据冗余和查询错误。ForeignKey是数据库级别的约束,不管通过什么渠道操作数据,都能保证关联关系的一致性。ORM层的语义明确性
加上ForeignKey能清晰告诉Room(以及其他阅读代码的开发者):这两个字段是关联到其他实体的外键,多对多关系的语义更明确,代码可读性更高。
内容的提问来源于stack exchange,提问作者eqtöck

