Docs 菜单

Docs 主页开发应用程序Atlas Device SDKs

创建 Realm 对象 - Kotlin SDK

在此页面上

  • 写事务(write transaction)
  • 托管和非托管对象
  • 创建 Realm 对象
  • 创建 Realm 对象
  • 创建嵌入式对象
  • 创建非对称对象
  • 创建 Realm 属性
  • 创建 RealmInstant(时间戳)属性
  • 创建 MutableRealmInt(计数器)属性
  • 创建 RealmAny(混合)属性
  • 创建collection属性
  • 创建 RealmList
  • 创建 RealmSet 属性
  • 创建字典属性
  • 创建关系属性
  • 创建对一关系
  • 创建对多关系
  • 创建反向关系
  • 创建 Realm 对象或集合的非托管副本

This page describes the concepts of write transactions and managed objects in a realm, then explains how to create and persist a new object to a local or synced realm using the Kotlin SDK. 要了解有关 Realm 对象以及如何定义它们的更多信息,请参阅Realm 对象。

您可以创建在打开域时将其Realm 对象类型包含在域模式中的对象。有关更多信息,请参阅打开 Realm打开同步 Realm。

注意

写入同步 Realm

对于本地 Realm 和同步 Realm,将新对象写入 Realm 的语法是相同的。 但是,还需要考虑其他因素来确定同步 Realm 中的写入操作是否成功。 有关更多信息,请参阅将数据写入同步 Realm - Kotlin SDK。

Realm 通过事务来处理写入。 所有写入都必须在事务中进行。 事务是一系列读取和写入操作,Realm 将其视为单个不可分割的操作:事务中的所有操作要么全部成功,要么全部生效。

Realm 将每个事务表示为包含零个或多个读写操作的回调函数。要运行事务,您需要定义事务回调并将其传递给 Realm 的write()writeBlocking()方法。在此回调中,您可以访问MutableRealm实例,然后创建、读取、更新和删除 Realm 中的对象。可变Realm 表示 Realm 文件的可写状态。可变 Realm 通过 realm.writerealm.writeBlocking方法自动提供和管理。

Realm 一次只允许一个打开的写事务。 Realm 阻止其他线程上的其他写入,直到可变 Realm 上的开放事务完成。因此,在事务中从 Realm 读取值时,不存在争用情况。

完成事务后,Realm 要么提交事务,要么取消事务:

  • 当 Realm 提交事务时,Realm 会将所有更改写入磁盘。 对于同步 Realm,SDK 会将更改排队以便与后端同步。

  • 当 Realm 取消写事务(write transaction)或事务中的操作导致错误时,所有更改都将被丢弃。

注意

冻结对象

当写事务(write transaction)完成时,从写入闭包返回的对象将成为冻结对象。有关更多信息,请参阅“冻结架构 - Kotlin SDK”。

Realm API 可能会将对象称为托管对象或非托管对象。 当您使用 Kotlin SDK 创建 Realm 对象时,在将其复制到 Realm(创建托管实例)之前,该对象是非托管的。

  • 托管对象是持久存在于 Realm 中的 Realm 对象。托管对象只能从开放 Realm 访问。 只要该域保持打开状态,就可以使用写事务(write transaction)中的更改来更新它们。托管对象绑定到它们源自的 Realm 实例,并且无法写入其他 Realm。

    您可以将 Realm API 与托管对象结合使用。例如,托管对象可以与其他对象建立关系,并观察其变化。您还可以创建托管对象的非托管副本,请参阅本页上的创建 Realm 对象或集合的非托管副本部分。

  • 非托管对象是 Realm 对象的实例,其行为与普通 Kotlin 对象相同,但不会持久保存在 Realm 中。 所有 Realm 对象都不是托管的,直到您在写事务中将它们复制到 Realm 中。 您不能将 Realm API 与非托管对象一起使用,也不能观察它们的更改。

提示

您可以使用isManaged()方法检查对象是否处于托管状态。

在创建新对象并将其保留到域之前,您必须定义新的Realm 对象类型。然后,在打开 Realm 时,将该对象类型包含在 Realm 模式中。

重要

Realm 对象类型必须位于模式中

您只能写入其Realm 对象类型包含在 域 模式中的对象。如果您尝试引用或写入不在您的模式中的Realm 对象类型,Realm 将返回模式验证错误。

要创建新对象并将其保留到 Realm,请执行以下操作:

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

  2. 使用类构造函数实例化非托管对象实例。您可以使用 apply 区块 可一次配置多个属性。

  3. 将非托管对象实例传递给copyToRealm()以将对象数据持久保存到 Realm。此方法返回对象的实时托管实例。

    重要

    非对称对象使用 Insert()

    非对称对象是特殊的只写对象,不会持久保存在 Realm 中。他们使用copyToRealm() 。相反,您可以在写事务中将非对称对象实例传递给insert()扩展方法。有关更多信息,请参阅本页上的“创建非对称对象”部分。

  4. 通过返回的实例使用持久化的 Realm 对象。 在写事务(write transaction)完成之前,可以访问活动对象。请注意,这不适用于不对称对象,非对称对象是只写的,不会持久保存到 Realm 中。

您还可以使用特定条件将更新或插入到 Realm 中。 有关更多信息,请参阅更新或插入 Realm 对象。

要创建新的RealmObject实例,请实例化新的Realm 对象类型。

在以下示例中,我们在realm.write()区块中实例化一个Frog对象,然后将实例化对象传递给copyToRealm()以返回托管实例:

// Open a write transaction
realm.write {
// Instantiate a new unmanaged Frog object
val unmanagedFrog = Frog().apply {
name = "Kermit"
age = 42
owner = "Jim Henson"
}
assertFalse(unmanagedFrog.isManaged())
// Copy the object to realm to return a managed instance
val managedFrog = copyToRealm(unmanagedFrog)
assertTrue(managedFrog.isManaged())
// Work with the managed object ...
}

要创建新的EmbeddedRealmObject实例,请将Realm 对象类型的实例分配给父对象的属性。这可以是一对一、一对多或反向关系,具体取决于您在父Realm 对象类型中定义嵌入式对象的方式。有关更多信息,请参阅定义嵌入式对象。

注意

嵌入式对象必须在父对象内创建

嵌入式对象需要一个父对象,并且不能作为独立的 Realm 对象存在。

嵌入式对象与其父对象具有严格的所有权关系。 创建嵌入式对象后,您无法将其重新分配给其他父对象,也无法在多个父对象之间共享。

在以下示例中,我们实例化一个带有嵌入式Address的新Contact对象,其中包含一个Contact对象和一个嵌入式Country对象:

realm.write {
// Instantiate a parent object with one embedded address
val contact = Contact().apply {
name = "Kermit"
address = EmbeddedAddress().apply {
propertyOwner = Contact().apply { name = "Mr. Frog" }
street = "123 Pond St"
country = EmbeddedCountry().apply { name = "United States" }
}
}
// Copy all objects to the realm to return managed instances
copyToRealm(contact)
}

我们还使用嵌入式Address对象列表实例化一个新的Business对象,其中也包含Contact对象和嵌入式Country对象:

realm.write {
// Instantiate a parent object with multiple embedded addresses
val localOffice = EmbeddedAddress().apply {
propertyOwner = Contact().apply { name = "Michigan J. Frog" }
street = "456 Lily Pad Ln"
country = EmbeddedCountry().apply { name = "United States" }
}
val remoteOffice = EmbeddedAddress().apply {
propertyOwner = Contact().apply { name = "Mr. Toad" }
street = "789 Leaping Frog Ave"
country = EmbeddedCountry().apply { name = "Ireland" }
}
val business = Business().apply {
name = "Big Frog Corp."
addresses = realmListOf(localOffice, remoteOffice)
}
// Copy all objects to the realm to return managed instances
copyToRealm(business)
}

版本 1.10.0 中的新增内容

与其他 Realm 对象不同,您使用copyToRealm()方法来创建 Realm 对象。 这是因为非对称对象是只写的:它们不会持久保存到 Realm 中。 相反,您可以使用特殊的insert()扩展方法将其插入到域中。

要创建新的AsymmetricRealmObject 实例,请使用 insert() 实例化 非对称对象类型 的新对象。

在以下示例中,我们实例化一个新的WeatherSensor对象,并在写事务(write transaction)中将其传递给insert()

// Open a write transaction
realm.write {
// Create a new asymmetric object
val weatherSensor = WeatherSensor().apply {
deviceId = "WX1278UIT"
temperatureInFarenheit = 6.7F
barometricPressureInHg = 29.65F
windSpeedInMph = 2
}
// Insert the object into the realm with the insert() extension method
insert(weatherSensor)
// WeatherSensor object is inserted into the realm, then synced to the
// App Services backend. You CANNOT access the object locally because it's
// deleted from the local realm after sync is complete.
}

插入后,非对称对象将同步到 App Services 后端和链接的 Atlas 数据库。 您无法在本地访问托管数据,也无法将其添加到 Realm 中或从 Realm 中删除,也无法对其进行查询。 有关如何在应用程序中使用非对称对象的信息,请参阅将数据流式传输到 Atlas - Kotlin SDK。

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

在以下示例中,我们有一个包含多个Frog Realm 属性的 Realm 对象类型:

class Frog : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId()
var name: String = ""
var birthdate: RealmInstant? = null
var fliesEaten: MutableRealmInt? = null
var favoriteThings: RealmList<RealmAny?> = realmListOf()
}

要使用RealmInstant属性创建新的对象实例,请使用以下任一方法实例化一个对象并将初始值传递给RealmInstant属性:

有关RealmInstant类型的更多信息,请参阅RealmInstant (Timestamp)。

在以下示例中,我们使用birthdate属性实例化一个新的Frog对象,并将初始值传递给RealmInstant.from()

realm.write {
// Instantiate a new unmanaged Frog object with a RealmInstant property
val frog = Frog().apply {
name = "Kermit"
// Set an initial value with RealmInstant.from() or RealmInstant.now()
birthdate = RealmInstant.from(1_577_996_800, 0)
}
// Copy the object to the realm to return a managed instance
copyToRealm(frog)
}

要使用MutableRealmInt属性创建新的对象实例,请实例化一个对象并使用MutableRealmInt.create()将初始值传递给MutableRealmInt属性。有关MutableRealmInt类型的更多信息,请参阅MutableRealmInt (Counter)。

在以下示例中,我们使用fliesEaten属性实例化一个新的Frog对象,并将初始值传递给MutableRealmInt.create()

realm.write {
// Instantiate a new unmanaged Frog object with a MutableRealmInt property
val frog = Frog().apply {
name = "Michigan J. Frog"
// Set an initial value with MutableRealmInt.create()
fliesEaten = MutableRealmInt.create(200)
}
// Copy the object to the realm to return a managed instance
copyToRealm(frog)
}

要创建具有多态RealmAny属性的新对象实例,请实例化一个对象并使用RealmAny.create()将受支持类型的初始值传递给RealmAny属性。有关RealmAny可以保存的值类型的列表,请参阅RealmAny(混合)。

在以下示例中,我们使用RealmAny类型的favoriteThings列表实例化一个新的Frog对象,并将初始值传递给RealmAny.create()

realm.write {
// Instantiate a new unmanaged Frog object with a RealmAny property
val frog = Frog().apply {
name = "Kermit"
// Set initial values with RealmAny.create()
favoriteThings = realmListOf(
RealmAny.create(42),
RealmAny.create("rainbows"),
RealmAny.create(Frog().apply {
name = "Kermit Jr."
})
)
}
// Copy the object to the realm to return a managed instance
copyToRealm(frog)
}

创建对象后,您必须知道存储的值类型才能使用RealmAny属性。 To learn how to update RealmAny properties after you create the object, refer to Update a RealmAny (Mixed) Property.

根据定义对象类型的方式,您可能会将属性定义为以下支持的集合类型之一:

  • RealmList

  • RealmSet

  • RealmDictionary

有关详细信息,请参阅定义集合属性。

集合是可变的:您可以在写事务中添加和删除集合中的元素。

collection可以包含托管和非托管对象。将集合复制到 Realm 时,您将创建集合的托管实例以及集合的所有元素,包括所有非托管元素。 非托管collection的行为与其相应的 Kotlin 类类似,并且不会持久保存到 域 中。

提示

侦听对已创建collection的更改

创建collection后,可以注册通知处理程序来侦听更改。有关更多信息,请参阅注册collection变更监听器。

要使用RealmList属性创建新的对象实例,请实例化一个对象并将支持类型的任何值传递给RealmList属性。有关RealmList可以保存的值类型的列表,请参阅RealmList。

您可以使用realmListOf()实例化非托管列表,或使用list.add()list.addAll()list.set()将元素传递到该列表。在将列表复制到 Realm 之前,该列表处于非托管状态。

在以下示例中,我们使用多个RealmList属性的初始值实例化一个新的Frog对象:

realm.write {
// Instantiate a new unmanaged Frog object with a RealmList property
val frog = Frog().apply {
name = "Kermit"
// Set values for each unmanaged list
favoritePonds.addAll(realmListOf(
Pond().apply { name = "Picnic Pond" },
Pond().apply { name = "Big Pond" }
))
favoriteForests.add(EmbeddedForest().apply { name = "Hundred Acre Wood" })
favoriteWeather = realmListOf("rain", "snow")
}
// Copy all objects to the realm to return managed instances
copyToRealm(frog)
}

要使用RealmSet属性创建新的对象实例,请实例化一个对象并将支持类型的任何值传递给RealmSet属性。有关RealmSet可以保存的有效类型的列表,请参阅RealmSet。

您可以使用realmSetOf()实例化非托管集,或使用set.add()set.addAll()将元素传递到该集。在您将其复制到 Realm 之前,该集合处于非托管状态。

在以下示例中,我们使用favoriteSnacksfavoriteWeather集属性的初始值实例化一个新的Frog对象:

realm.write {
// Instantiate a new unmanaged Frog object with RealmSet properties
val frog = Frog().apply {
name = "Kermit"
// Set initial values to each unmanaged set
favoriteSnacks.addAll(setOf(
Snack().apply { name = "flies" },
Snack().apply { name = "crickets" },
Snack().apply { name = "worms" }
))
favoriteWeather.add("rain")
}
// Copy all objects to the realm to return managed instances
copyToRealm(frog)
}

要使用RealmDictionary属性创建新的对象实例,请实例化一个对象并将受支持类型的任何键值对传递给RealmDictionary属性。 RealmDictionary只接受String键,但值可以是非字符串类型。有关有效类型的列表,请参阅RealmMap/RealmDictionary。

您可以使用realmDictionaryOf()realmDictionaryEntryOf()实例化非托管字典。或者,您可以使用put()putAll()传递键值。在将字典复制到 Realm 之前,该字典处于非托管状态。

在以下示例中,我们使用多个字典属性的初始键值实例化一个新的Frog对象:

realm.write {
val frog = Frog().apply {
name = "Kermit"
// Set initial key-values to each unmanaged dictionary
favoriteFriendsByPond = realmDictionaryOf(
"Picnic Pond" to Frog().apply { name = "Froggy Jay" },
"Big Pond" to Frog().apply { name = "Mr. Toad" }
)
favoriteTreesInForest["Maple"] = EmbeddedForest().apply {
name = "Hundred Acre Wood"
}
favoritePondsByForest.putAll(
mapOf(
"Silver Pond" to "Big Forest",
"Big Lake" to "Elm Wood",
"Trout Pond" to "Sunny Wood"
)
)
}
// Copy all objects to the realm to return managed instances
copyToRealm(frog)
}

Realm 不允许在映射键中使用 .$ 字符。您可以使用百分比编码和解码来存储包含这些不允许的任一字符的映射键。

// Percent encode . or $ characters to use them in map keys
val mapKey = "Hundred Acre Wood.Northeast"
val encodedMapKey = "Hundred Acre Wood%2ENortheast"

根据您定义的 Realm 对象类型,您可能具有引用另一个 Realm 对象的属性。这可以是对一、对多或反向关系。 有关在对象模型中定义关系的更多信息,请参阅定义关系。

您还可以将一个 Realm 对象直接嵌入另一个 Realm 对象,以创建具有EmbeddedRealmObject类型的嵌套数据结构。要创建与嵌入式对象的关系,请参阅本页上的创建嵌入式对象部分。

要创建具有一对一关系属性的新对象实例,请实例化这两个对象并将引用的对象传递给关系属性。

在以下示例中,我们使用引用Frog 对象的favoritePond 属性和引用另一个Pond bestFriendFrog对象的 属性来实例化一个新的 对象:

realm.write {
// Instantiate a new unmanaged Frog object with to-one
// relationship with a Realm object
val frog = Frog().apply {
name = "Kermit"
age = 12
favoritePond = Pond().apply { name = "Picnic Pond" }
bestFriend = Frog().apply { name = "Froggy Jay" }
}
// Copy all objects to the realm to return managed instances
copyToRealm(frog)
}

要创建具有对多关系属性的新对象实例,请实例化所有对象并将任何引用的对象传递给关系集合属性。

在以下示例中,我们使用引用一组Forest frogsThatLiveHereFrog对象的 属性和引用nearByPonds Pond对象列表的 属性来实例化一个新的 对象:

realm.write {
// Instantiate a new unmanaged Forest object with to-many
// relationship with multiple Realm objects
val forest = Forest().apply {
name = "Froggy Forest"
frogsThatLiveHere = realmSetOf(
Frog().apply { name = "Kermit" },
Frog().apply { name = "Froggy Jay" }
)
nearbyPonds = realmListOf(
Pond().apply { name = "Small Picnic Pond" },
Pond().apply { name = "Big Pond" }
)
}
// Copy all objects to the realm to return managed instances
copyToRealm(forest)
}

要创建具有关系属性的新对象实例,请实例化父对象并将任何引用的子对象传递给反向链接collection属性。

在以下示例中,我们实例化一个新的User对象,该对象具有引用Post对象列表的反向链接posts属性:

realm.write {
// Instantiate a new unmanaged User object with to-many
// relationship with multiple Realm objects
val post1 = Post().apply {
title = "Forest Life"
}
val post2 = Post().apply {
title = "Top Ponds of the Year!"
}
val user = User().apply {
name = "Kermit"
posts = realmListOf(post1, post2)
}
// Copy all objects to the realm to return managed instances
copyToRealm(user)
}

创建对象后,可以访问反向链接collection属性以获取子对象,但不能直接修改反向链接本身。有关更多信息,请参阅更新反向关系。

您可以将托管对象或集合传递给copyFromRealm()来创建该托管对象或集合的非托管副本。此方法返回对象或集合的非托管内存中副本。对于集合,这是一个深层副本,其中包括直到指定的depth的所有引用对象。

在以下示例中,我们创建现有托管Pond对象的非托管副本,其中包含两个Frog对象的列表。 从 域 复制对象后,我们确认副本是非托管的,并且包含两个引用的Frog对象:

realm.writeBlocking {
// Fetch the managed object you want to copy
val managedPond = query<Pond>("name == $0", "Big Pond").find().first()
assertTrue(managedPond.isManaged())
// Create an unmanaged copy of the object
val unmanagedPond = copyFromRealm(managedPond)
assertFalse(unmanagedPond.isManaged())
Log.v("Unmanaged pond name: ${unmanagedPond.name}")
// Confirm the unmanaged copy contains all elements
// in the copied object's RealmList
val unmanagedFrogs = unmanagedPond.frogsThatLiveHere
assertFalse(unmanagedFrogs[0].isManaged())
assertFalse(unmanagedFrogs[1].isManaged())
assertEquals(2, unmanagedFrogs.size)
Log.v("Unmanaged frogs: ${unmanagedFrogs[0].name}, ${unmanagedFrogs[1].name}")
}
Unmanaged pond name: Big Pond
Unmanaged frogs: Kermit, Froggy Jay
← 读取和写入数据 - Kotlin SDK