更新 Realm 对象 - Kotlin SDK
在此页面上
本页介绍如何使用 Kotlin SDK 更新本地或同步 Realm 中的现有 Realm 对象。要了解在 Realm 中创建对象的详情,请参阅创建 Realm 对象 - Kotlin SDK。
Realm 支持对 Realm 对象和嵌入式对象进行更新和更新插入操作。更新或插入操作会插入对象的新实例,或更新符合特定条件的现有对象。有关更多信息,请参阅本页上的 “更新或插入 Realm 对象”部分。
您无法更新不对称对象。这是因为不对称对象属于特殊的只写对象,不会持久保存在 Realm 中。有关如何在应用程序中使用不对称对象的信息,请参阅将数据流式传输到 Atlas - Kotlin SDK。
注意
写入同步 Realm
对于本地和同步 Realm,更新 Realm 中的对象的语法是相同的。但是,还需要考虑其他因素来确定同步 Realm 中的写入操作是否成功。请参阅向同步 Realm 中写入对象 - Kotlin SDK,了解详情。
更新操作
所有修改 Realm 的操作(包括更新和更新插入操作)都必须在写事务内执行。写入事务会传递给 Realm 的write()或writeBlocking()方法。在此回调中,您可以访问MutableRealm实例,然后更新 Realm 中的对象。有关写事务以及 Realm 如何处理这些事务的更多信息,请参阅写事务。
此外,您只能修改活动对象,这些对象只能在写事务内部访问。 您可以在事务中使用mutableRealm.findLatest() 将冻结对象转换为活动对象。
例子
修改前转换冻结对象
val frozenFrog = realm.query<Frog>().find().first() // Open a write transaction realm.write { // Get the live frog object with findLatest(), then update it findLatest(frozenFrog)?.let { liveFrog -> liveFrog.name = "Kermit" liveFrog.age -= 1 } }
更新现有对象
要修改存储在 Realm 中的 Realm 对象或嵌入式对象的属性,请执行以下操作:
使用realm.write()打开写事务 或realm.writeBlocking()。
使用query() 在事务的可变 Realm 中查询要修改的对象,从而获取活动对象:
将对象类型指定为传递给
query()
的类型参数。(可选)通过指定查询来筛选返回的对象集。如果不包含查询筛选器,则返回指定类型的所有对象。有关使用 Kotlin SDK 进行查询的更多信息,请参阅读取 Realm 对象 - Kotlin SDK。
重要
对象必须是实时的
您只能修改活动对象。如果查询发生在写事务之外,则必须使用
mutableRealm.findLatest()
将冻结对象转换为事务中的活动对象。修改写事务中的对象。您可以使用 apply 区块 可一次配置多个属性。当 Realm 提交写事务时,所有更改都会自动保存到 Realm 中。
注意
更新字符串或字节数组
由于 Realm 将字段作为一个整体进行操作,因此无法直接更新字符串或字节数组的单个元素。相反,您必须读取整个字段,修改单个元素,然后在事务区块中再次写回整个字段。
更新 Realm 对象
要更新 Realm 对象,请使用返回筛选器查询类型,该筛选器将返回待更新对的特定对象。然后,修改对象属性。
在以下示例中,我们通过主键查询 Frog
对象,然后更新 name
和 age
属性:
// Query and update a realm object in a single write transaction realm.write { val liveFrog = query<Frog>("_id == $0", PRIMARY_KEY_VALUE).find().first() liveFrog.name = "Michigan J. Frog" liveFrog.age += 1 }
更新嵌入式对象
更新嵌入式对象的两种方式:修改单个属性或覆盖整个嵌入式对象。
要更新嵌入式对象中的一个或多个属性,可获取父对象或嵌入式对象,并在写事务中重新分配嵌入式对象属性。
下面示例中的 Contact
对象包含嵌入式 EmbeddedAddress
对象。我们通过几个操作来更新嵌入式对象的属性:
// Modify embedded object properties in a write transaction realm.write { // Fetch the objects val addressToUpdate = findLatest(address) ?: error("Cannot find latest version of embedded object") val contactToUpdate = findLatest(contact) ?: error("Cannot find latest version of parent object") // Update a single embedded object property directly addressToUpdate.street = "100 10th St N" // Update multiple properties addressToUpdate.apply { street = "202 Coconut Court" city = "Los Angeles" state = "CA" postalCode = "90210" } // Update property through the parent object contactToUpdate.address?.state = "NY" }
要完全覆盖嵌入式对象,请在写入事务中将新的嵌入式对象实例分配给父属性。此举将删除现有的嵌入式对象。
下面示例中的 Contact
对象包含嵌入式 EmbeddedAddress
对象。我们定义一个新的 EmbeddedAddress
对象并将其分配给父属性:
realm.write { val parentObject = query<Contact>("_id == $0", PRIMARY_KEY_VALUE).find().first() val newAddress = EmbeddedAddress().apply { propertyOwner = Contact().apply { name = "Michigan J. Frog" } street = "456 Lily Pad Ln" country = EmbeddedCountry().apply { name = "Canada" } } // Overwrite the embedded object with the new instance (deletes the existing object) parentObject.address = newAddress }
更新多个对象
还可以更新一个 Realm 中的多个对象:
使用realm.write()打开写事务 或realm.writeBlocking()。
更新查询返回的 RealmResults 集合的元素。
val tadpoles = realm.query<Frog>("age <= $0", 2) for (tadpole in tadpoles.find()) { realm.write { findLatest(tadpole)?.name = tadpole.name + " Jr." } }
更新 Realm 属性
根据您定义对象类型的方式,您可能具有特定于 Realm 的特殊类型的属性。
更新 MutableRealmInt(计数器)属性
可以使用以下 Realm API 函数来更新 MutableRealmInt
属性值:
increment()
decrement()
set()
这些方法返回带有变更值的 MutableRealmInt
属性。
提示
谨慎使用 set() 操作符
set()
操作符会覆盖之前对 increment()
或 decrement()
的任何调用。不建议将 set()
与 increment()
或 decrement()
混合使用,除非使用案例可以接受模糊计数。
除了 Realm API 函数之外,您还可以使用以下一组操作符和中缀函数,它们与 Kotlin 标准库 Long
提供的函数类似: 对于 :
一元前缀操作符:
unaryPlus
、unaryMinus
递增和递减操作符:
inc
、dec
(不要与increment
和decrement
混淆)算术操作符:
plus
、minus
、times
、div
、rem
相等操作符:
equals
比较操作符:
compareTo
Bitwise functions:
shl
,shr
,ushr
,and
,or
,xor
,inv
但是,这些操作符和中缀函数不会改变调用它们的实例。相反,它们会返回一个新的非托管实例以及该操作的结果。
重要
只有 Realm 方法才会产生变更值
唯一会产生变异值的操作是 Realm API 函数:increment
、decrement
和 set
。所有其他操作(包括 inc
和 dec
)都会返回一个带有新值的非托管实例。
在以下示例中,我们使用 increment()
、decrement()
和 set()
操作更新 MutableRealmInt
属性:
// Open a write transaction realm.write { // Get the live object val frog = query<Frog>("_id == $0", PRIMARY_KEY_VALUE).find().first() val counter: MutableRealmInt? = frog.fliesEaten counter?.get() // 1 // Increment the value of the MutableRealmInt property // ** Note use of decrement() with negative value ** counter?.increment(0) // 1 counter?.increment(5) // 6 counter?.decrement(-2) // 8 // Decrement the value of the MutableRealmInt property // ** Note use of increment() with negative value ** counter?.decrement(0) // 8 counter?.decrement(2) // 6 counter?.increment(-1) // 5 // Set the value of the MutableRealmInt property // ** Use set() with caution ** counter?.set(0)// 0 }
更新 RealmAny(混合)属性
RealmAny属性不可变。要更新RealmAny
值,必须使用所需值和类型创建该属性的新实例。有关添加RealmAny
属性的更多信息,请参阅创建 RealmAny(混合)属性。
此外,您必须知道存储的类型,才能从RealmAny
属性中提取该值。如果调用错误类型的 getter 方法,Realm 会抛出异常。
提示
使用条件表达式处理多态性
因为您必须知道存储的类型才能提取其值,所以我们建议使用 when
表达式来处理 RealmAny
类型及其可能的内部值类。
val favoriteThings = frog.favoriteThings when (favoriteThings.type) { INT -> rating(favoriteThings.asInteger()) STRING -> description(favoriteThings.asString()) BYTE_ARRAY -> image(favoriteThings.asByteArray()) // Handle other possible types... else -> { // Debug or a perform default action Log.d("ExampleHandler", "Unhandled type: ${favoriteThings.type}") } }
在以下示例中,我们通过创建每个列表元素的新实例来更新包含 RealmAny
属性列表的 Frog
对象:
// Find favoriteThing that is an Int // Convert the value to Double and update the favoriteThing property realm.write { val kermit = query<Frog>().find().first() val realmAny: RealmList<RealmAny?> = kermit.favoriteThings for (i in realmAny.indices) { val thing = realmAny[i] if (thing?.type == RealmAny.Type.INT) { val intValue = thing.asInt() val doubleValue = intValue.toDouble() realmAny[i] = RealmAny.create(doubleValue) } } } // Overwrite all existing favoriteThing properties // ** Null clears the property value ** realm.write { val frog = query<Frog>().find().first() val realmAny: RealmList<RealmAny?> = frog.favoriteThings realmAny[0] = RealmAny.create("sunshine") realmAny[1] = RealmAny.create(Frog().apply { name = "Kermit Sr." }) realmAny[2] = null }
注意
使用空值清除 RealmAny 属性值
您可以将 null
直接分配给 RealmAny
属性,以删除当前值。
更新集合属性
根据定义对象类型的方式,您可能会将属性定义为以下支持的集合类型之一:
RealmList
RealmSet
RealmDictionary
集合是可变的,并由其相应的内置 Kotlin 类支持。您可以在写入事务中添加和删除集合中的元素。
更新 RealmList
您可以像更新 Kotlin MutableList 一样 更新 RealmList 中的元素。
在下面示例中,我们更新现有 Frog
对象的 RealmList
元素:
realm.write { // Get the live object val realmList = query<Frog>("name = $0", "Kermit").first().find()!!.favoritePonds realmList[0].name = "Picnic Pond" realmList.set(1, Pond().apply { name = "Big Pond" }) }
有关添加和删除 RealmList
元素的更多信息,请参阅创建 RealmList 和从 RealmList 中删除元素。
更新 RealmSet
您可以在 RealmSet 中添加、更新和删除元素,就像在 Kotlin MutableSet 中一样。
在下面的示例中,我们遍历并更新现有 Frog
对象的 RealmSet
元素:
// Find a frog in the realm val kermit = realm.query<Frog>("name = $0", "Kermit").find().first() val realmSet = kermit.favoriteSnacks // Update the name of each snack in the set for (snack in realmSet) { realm.write { findLatest(snack)?.name = snack.name.uppercase() } } realm.write { // Find all frogs who like rain val frogsWhoLikeRain = realm.query<Frog>("favoriteWeather CONTAINS $0", "rain").find() // Add thunderstorms to their favoriteWeather set for (frog in frogsWhoLikeRain) { val latestFrog = findLatest(frog) latestFrog?.favoriteWeather?.add("thunderstorms") } }
有关添加和删除 RealmSet
元素的更多信息,请参阅创建 RealmSet 属性和从 RealmSet 中删除元素。
更新字典
您可以像更新 Kotlin MutableMap 一样 更新 RealmDictionary 中的键和值。
在下面的示例中,我们更新 RealmDictionary
// Find frogs who have forests with favorite ponds val thisFrog = realm.query<RealmDictionary_Frog>("favoritePondsByForest.@count > 1").find().first() // Update the value for a key if it exists if (thisFrog.favoritePondsByForest.containsKey("Hundred Acre Wood")) { realm.write { findLatest(thisFrog)?.favoritePondsByForest?.set("Lothlorien", "Lily Pad Pond") } } // Add a new key-value pair realm.write { findLatest(thisFrog)?.favoritePondsByForest?.put("Sherwood Forest", "Miller Pond") }
更新关系
根据您定义对象类型的方式,您可能会有引用另一个 Realm 对象的属性。这可以是对一、对多或反向关系。
您还可以将一个 Realm 对象直接嵌入另一个 Realm 对象,创建带有 EmbeddedRealmObject
类型的嵌套数据结构。有关详情,请参阅本页面的更新嵌入式对象部分。
更新对一关系
您可以通过修改定义关系的属性来更新 Realm 中对象之间的关系,就像更新任何其他属性一样。
在以下示例中,我们有一个 Frog
对象,该对象具有引用单个 Pond
对象的 favoritePond
属性和引用另一个 Frog
对象的 bestFriend
属性:
realm.write { val kermit = query<Frog>("name == $0", "Kermit").find().first() // Update a property on the related object kermit.favoritePond?.name = "Big Pond" // Assign a new related object val newBestFriend = Frog().apply { name = "Froggy Jr." } kermit.bestFriend = newBestFriend }
更新对多关系
您可以像更新任何其他集合一样更新对多关系。 请参阅本页的“更新集合属性”部分。
在以下示例中,我们有一个 Forest
对象,该对象具有引用一组 Frog
对象的 frogsThatLiveHere
属性和引用一组 Pond
对象的 nearByPonds
属性:
realm.write { val forest = query<Forest>().find().first() // Update a property on a related object in the set forest.frogsThatLiveHere.first().name = "Kermit Sr." // Add a new related object to the list forest.frogsThatLiveHere.add(Frog().apply { name = "Froggy Jr." }) }
更新反向关系
您可以反向访问和更新关系中的对象。不过,您不能直接修改反向链接集合本身。相反,当您修改任何相关对象时,Realm 都会自动更新隐式关系。有关详细信息,请参阅定义反向关系。
在以下示例中,我们有一个父 User
对象,该对象带有引用 Post
子对象列表的反向链接 posts
属性。我们通过反向链接更新父对象和子对象的属性:
realm.write { // Update child objects through the parent val parent = query<User>().find().first() parent.posts[0].title = "Forest Life Vol. 2" parent.posts.add(Post().apply { title = "Forest Life Vol. 3" }) // Update child objects (Realm automatically updates the backlink collection) val child = query<Post>("title == $0", "Top Ponds of the Year!").find().first() child.title = "Top Ponds of the Year! Vol. 2" assertEquals("Top Ponds of the Year! Vol. 2", parent.posts[1].title) // Update the parent object through the child child.user[0].name = "Kermit Sr." // ** You CANNOT directly modify the backlink collection ** val readOnlyBacklink: RealmResults<User> = child.user }
更新或插入 Realm 对象
要将对象更新或插入域,请使用 copyToRealm() 插入带有主键的对象,就像创建新对象一样,然后传递 UpdatePolicy 参数,指示 SDK 如何处理带有相同主键的现有对象:
UpdatePolicy.ALL
:更新任何以相同主键标识的现有对象的所有属性。UpdatePolicy.ERROR
(默认):禁止更新现有对象,如果已存在具有相同主键的对象,则抛出异常。如果您不指定更新策略,Realm 默认使用该策略。
根据更新策略,可能会发生以下情况:
如果不存在与主键匹配的对象,则 SDK 会插入新对象。
如果存在带有相同主键的对象,则 SDK 可以执行以下任一操作:
更新任何以相同主键标识的现有对象的所有属性。请注意,即使属性已更新为相同的值,也会在变更监听器中将属性标记为已更新。
抛出异常,表明 Realm 中已存在对象。
在下面的示例中,我们尝试插入一个 Frog
对象,该对象的主键已存在于具有 UpdatePolicy.ALL
的 Realm 中,并确认该对象已成功更新或插入:
realm.write { val existingFrog = query<Frog>("_id == $0", PRIMARY_KEY_VALUE).find().first() assertEquals(existingFrog.name, "Kermit") // Use copyToRealm() to insert the object with the primary key // ** UpdatePolicy determines whether to update or throw an error if object already exists** copyToRealm(Frog().apply { _id = PRIMARY_KEY_VALUE name = "Wirt" age = 4 species = "Greyfrog" owner = "L'oric" }, UpdatePolicy.ALL) val upsertFrog = query<Frog>("_id == $0", PRIMARY_KEY_VALUE).find().first() assertEquals(upsertFrog.name, "Wirt") }