You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Room数据库(Kotlin)中用外键关联多张表?及报错排查

Kotlin Room多表外键关联修正方案

错误原因分析

SQLite外键约束明确要求:被引用的父表字段必须是主键或带有唯一索引。你当前的问题是FieldTypeModel的外键错误引用了FieldModel的field_type字段,该字段既不是主键也无唯一约束,同时表结构的关联逻辑不符合字典表的设计规范——字段类型表应该是被字段表引用,而非反过来。

正确表结构修正

1. 调整关联逻辑(推荐方案)

FieldTypeModel作为字段类型字典表,应该被FieldModel引用:即FieldModel的字段关联FieldTypeModel的主键id,而非反向关联。修正后的实体类代码如下:

/* 表1:文档类型表 */
@Entity(tableName = "Doc_Type_Table")
data class DocTypeModel(
    @ColumnInfo(name = "title")
    var title: String,
    @ColumnInfo(name = "date")
    var date: String,
    @ColumnInfo(name = "color_code")
    var colorCode: String,
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Int = 0
}

/* 表2:字段类型字典表(先定义,方便被引用) */
@Entity(tableName = "Field_Type_Table")
data class FieldTypeModel(
    @ColumnInfo(name = "type")
    var fieldType: String // 存储字段类型名称,比如"文本"、"数字"等
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Int = 0 // 主键作为被引用的唯一标识
}

/* 表3:字段表,关联文档类型和字段类型 */
@Entity(
    tableName = "Field_Table",
    foreignKeys = [
        // 关联文档类型表
        ForeignKey(
            entity = DocTypeModel::class,
            childColumns = ["doc_type_id"],
            parentColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        ),
        // 关联字段类型表
        ForeignKey(
            entity = FieldTypeModel::class,
            childColumns = ["field_type_id"],
            parentColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        )
    ],
    // 可选:给外键字段加索引,提升查询性能
    indices = [Index(value = ["doc_type_id"]), Index(value = ["field_type_id"])]
)
data class FieldModel(
    @ColumnInfo(name = "doc_type_id")
    var docTypeId: Int,
    @ColumnInfo(name = "field_type_id")
    var fieldTypeId: Int, // 关联字段类型表的主键
    @ColumnInfo(name = "name")
    var name: String,
    @ColumnInfo(name = "value")
    var value: String,
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Int = 0
}

2. 若坚持原有关联方向(不推荐)

如果因特殊需求必须让FieldTypeModel引用FieldModel的field_type字段,需给FieldModel的field_type添加唯一索引,代码修改如下:

@Entity(
    tableName = "Field_Table",
    foreignKeys = [
        ForeignKey(
            entity = DocTypeModel::class,
            childColumns = ["doc_type_id"],
            parentColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        )
    ],
    // 添加唯一索引
    indices = [Index(value = ["field_type"], unique = true)]
)
data class FieldModel(
    @ColumnInfo(name = "doc_type_id")
    var docTypeId: Int,
    @ColumnInfo(name = "name")
    var name: String,
    @ColumnInfo(name = "field_type")
    var fieldType: Int,
    @ColumnInfo(name = "value")
    var value: String,
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Int = 0
}

注意:此方案会限制FieldModel中每个field_type值只能存在一条记录,不符合常规业务逻辑——同一字段类型应允许对应多个字段,因此不推荐使用。

关联数据查询示例

如果需要查询包含关联数据的结果,可通过Room的@Embedded和@Relation定义数据类:

// 文档类型包含其下所有字段,每个字段包含对应的类型信息
data class DocTypeWithFields(
    @Embedded val docType: DocTypeModel,
    @Relation(
        parentColumn = "id",
        entityColumn = "doc_type_id",
        entity = FieldModel::class
    )
    val fields: List<FieldWithType>
)

data class FieldWithType(
    @Embedded val field: FieldModel,
    @Relation(
        parentColumn = "field_type_id",
        entityColumn = "id",
        entity = FieldTypeModel::class
    )
    val fieldType: FieldTypeModel
)

在Dao中定义查询方法:

@Dao
interface DocDao {
    @Transaction
    @Query("SELECT * FROM Doc_Type_Table")
    suspend fun getAllDocTypesWithFields(): List<DocTypeWithFields>
}

内容的提问来源于stack exchange,提问作者Fahad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 16:15:41