Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ / /

更新 Realm 对象 - Kotlin SDK

在此页面上

  • 更新操作
  • 更新现有对象
  • 更新 Realm 对象
  • 更新嵌入式对象
  • 更新多个对象
  • 更新 Realm 属性
  • 更新 MutableRealmInt(计数器)属性
  • 更新 RealmAny(混合)属性
  • 更新集合属性
  • 更新 RealmList
  • 更新 RealmSet
  • 更新字典
  • 更新关系
  • 更新对一关系
  • 更新对多关系
  • 更新反向关系
  • 更新或插入 Realm 对象

本页介绍如何使用 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 对象或嵌入式对象的属性,请执行以下操作:

  1. 使用realm.write()打开写事务 或realm.writeBlocking()。

  2. 使用query() 在事务的可变 Realm 中查询要修改的对象,从而获取活动对象:

    1. 将对象类型指定为传递给 query() 的类型参数。

    2. (可选)通过指定查询来筛选返回的对象集。如果不包含查询筛选器,则返回指定类型的所有对象。有关使用 Kotlin SDK 进行查询的更多信息,请参阅读取 Realm 对象 - Kotlin SDK

    重要

    对象必须是实时的

    您只能修改活动对象。如果查询发生在写事务之外,则必须使用 mutableRealm.findLatest() 将冻结对象转换为事务中的活动对象。

  3. 修改写事务中的对象。您可以使用 apply 区块 可一次配置多个属性。当 Realm 提交写事务时,所有更改都会自动保存到 Realm 中。

注意

更新字符串或字节数组

由于 Realm 将字段作为一个整体进行操作,因此无法直接更新字符串或字节数组的单个元素。相反,您必须读取整个字段,修改单个元素,然后在事务区块中再次写回整个字段。

要更新 Realm 对象,请使用返回筛选器查询类型,该筛选器将返回待更新对的特定对象。然后,修改对象属性。

提示

使用独特的识别信息

我们建议使用唯一的识别信息(例如主键值)进行过滤,以确保查询返回正确的对象。

在以下示例中,我们通过主键查询 Frog 对象,然后更新 nameage 属性:

// 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 中的多个对象:

  1. 使用realm.query() 在 Realm 中查询对象集合。

  2. 使用realm.write()打开写事务 或realm.writeBlocking()。

  3. 更新查询返回的 RealmResults 集合的元素。

val tadpoles = realm.query<Frog>("age <= $0", 2)
for (tadpole in tadpoles.find()) {
realm.write {
findLatest(tadpole)?.name = tadpole.name + " Jr."
}
}

根据您定义对象类型的方式,您可能具有特定于 Realm 的特殊类型的属性。

可以使用以下 Realm API 函数来更新 MutableRealmInt 属性值:

  • increment()

  • decrement()

  • set()

这些方法返回带有变更值的 MutableRealmInt 属性。

提示

谨慎使用 set() 操作符

set() 操作符会覆盖之前对 increment()decrement() 的任何调用。不建议将 set()increment()decrement() 混合使用,除非使用案例可以接受模糊计数。

除了 Realm API 函数之外,您还可以使用以下一组操作符和中缀函数,它们与 Kotlin 标准库 Long提供的函数类似: 对于 :

  • 一元前缀操作符:unaryPlusunaryMinus

  • 递增和递减操作符:incdec(不要与 incrementdecrement 混淆)

  • 算术操作符:plusminustimesdivrem

  • 相等操作符: equals

  • 比较操作符: compareTo

  • Bitwise functions: shl, shr, ushr, and, or, xor, inv

但是,这些操作符和中缀函数不会改变调用它们的实例。相反,它们会返回一个新的非托管实例以及该操作的结果。

重要

只有 Realm 方法才会产生变更值

唯一会产生变异值的操作是 Realm API 函数:incrementdecrementset。所有其他操作(包括 incdec)都会返回一个带有新值的非托管实例。

在以下示例中,我们使用 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 属性中提取该值。如果调用错误类型的 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 类支持。您可以在写入事务中添加和删除集合中的元素。

提示

监听集合的变更

您可以注册通知处理程序来侦听更改。有关详细信息,请参阅注册集合更改侦听器。

您可以像更新 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 中添加、更新和删除元素,就像在 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")
}

有关添加和删除 RealmDictionary 条目的详情,请参阅创建字典属性删除字典键/值

根据您定义对象类型的方式,您可能会有引用另一个 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
}

要将对象更新或插入域,请使用 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")
}

后退

读取