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

Realm Kotlin SDK多对多关系关联已存在Tag报错问题排查

问题排查:Realm多对多关联保存时重复主键异常

问题场景与代码

我尝试在Record与Tag对象间实现多对多关系,每个Record可关联多个Tag,对应的Kotlin实体类代码如下:

open class Record : RealmObject {
    @PrimaryKey
    var id: String = ""
    var title: String = ""
    var description: String = ""

    var tags: RealmList<Tag> = realmListOf()
}

open class Tag : RealmObject {
    @PrimaryKey
    var id: String = ""
    var name: String = ""

    val record: RealmResults<Record> by backlinks(Record::tags)
}

测试保存Record对象时发现:保存关联不存在于Tags表的Tag时,该Tag会被自动添加;但保存关联已存在的Tag时,会抛出异常:

Attempting to create an object of type 'Tag' with an existing primary key value '1'

保存带Tag列表的Record对象的代码如下:

val record = Record().apply {
    id = randomUUID()
    title = "TestRecord"
    description = "Desc"
    tags = realmListOf(
        Tag().apply {
            id = "1"
            name = "Tag1"
        },
        Tag().apply {
            id = "2"
            name = "Tag2"
        }
    )
}
realm.write {
    copyToRealm(record)
}

问题原因

你直接创建了未被Realm管理的新Tag对象并添加到Record的tags列表,调用copyToRealm()时,Realm会尝试将这些新Tag写入数据库。如果数据库中已存在同主键的Tag,就会触发重复主键异常——因为copyToRealm()仅支持写入未持久化的新对象,无法直接关联已存在的持久化对象。

解决方案

要正确关联已存在的Tag,需先从Realm中获取对应的持久化对象实例,再添加到Record的列表;对于不存在的Tag,则创建新对象写入。以下两种方式均可解决:

方式一:用copyToRealmOrUpdate()批量处理Tag

在写入Record前,先对每个Tag执行copyToRealmOrUpdate(),确保Tag要么被创建要么被更新,再关联到Record:

val record = Record().apply {
    id = randomUUID()
    title = "TestRecord"
    description = "Desc"
}
realm.write {
    // 处理每个Tag:存在则更新,不存在则创建,返回Realm管理的对象
    val tag1 = copyToRealmOrUpdate(Tag().apply {
        id = "1"
        name = "Tag1"
    })
    val tag2 = copyToRealmOrUpdate(Tag().apply {
        id = "2"
        name = "Tag2"
    })
    // 将持久化后的Tag添加到Record列表
    record.tags.addAll(listOf(tag1, tag2))
    // 写入Record
    copyToRealm(record)
}

方式二:先查询已存在的Tag,不存在再创建

先查询数据库中是否有对应主键的Tag,有则直接使用查询结果,没有则创建新对象:

val record = Record().apply {
    id = randomUUID()
    title = "TestRecord"
    description = "Desc"
}
realm.write {
    val tagIds = listOf("1", "2")
    val tags = tagIds.map { tagId ->
        // 查询已存在的Tag
        findLatest(Tag::class, tagId) ?: 
        // 不存在则创建新Tag并写入
        copyToRealm(Tag().apply {
            id = tagId
            name = "Tag$tagId"
        })
    }
    record.tags.addAll(tags)
    copyToRealm(record)
}

关键注意点

  • copyToRealm():仅能写入未被Realm管理的新对象,若对象主键已存在则抛出异常。
  • copyToRealmOrUpdate():根据主键判断,存在则更新对象,不存在则创建,适合处理需要同步的关联对象。
  • 关联已存在的Realm对象时,必须使用Realm已管理的对象实例,不能用自己new出来的未持久化对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:10:08