I'm trying to implement a many-to-many relationship between Record and Tag objects. Each Record can have multiple tags.
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)
}
When testing the saving of Record objects, I noticed the following issue. In the case where we save a Record with a Tag that doesn't exist in the Tags table, that tag is automatically added. However, if we try to save a Record with a tag that already exists in the Tags table, we get the following exception - "Attempting to create an object of type 'Tag' with an existing primary key value '1'".
I write a Record object with a list of Tags as follows:
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)
}
Please help me figure out what I'm doing wrong?
I believe the issue here is the understanding of how Realm objects interact; unmanaged vs managed
Let me address this at a high level with some pseudo code
Suppose theres a Record and Tag objects and nothing yet exists in your Realm database. Record has a RealmList of Tags
Create a Tag and add it to a Record.
var t0 = Tag(withPrimaryKey: 0)
var myRecord = Record()
myRecord.myTags.add(t0)
realm.add(myRecord) //success since it doesn't exist
This will succeed and both a tag and record will be added to Realm! But then later we do this
let myRecord = read a record from Realm
var t0 = Tag(withPrimaryKey: 0)
myRecord.myTags.add(t0) //fail as this primary key exists
The above will fail because the Tag with primary key 0 already exists, and primary keys must be unique
However, assume the Realm database has only one entry, a Tag with primary key of 0
let t0 = read Tag with primary key 0 from realm
let myRecord = Record()
myRecord.myTags.add(t0)
realm.add(myRecord) //success since t0 was already managed
will succeed and the Tag with primary key 0 will NOT be duplicated. Since it was already a managed object (e.g. stored in realm), it's simply added to the myRecord, myTags property as a reference to the managed object.
If you know a specific objects primary key, you can also Upsert it; that process will either create the object if it doesn't exist, or modify the object if it does.