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

Kotlin集成Realm:如何仅插入不存在的嵌套RealmObject

通用解决方案

1. 核心思路

利用Realm查询API结合反射,封装通用函数自动处理带主键对象的复用/插入:检查数据库中是否存在对应主键的实例,存在则直接复用,不存在则插入新实例。既避免UpdatePolicy.ALL带来的无意义更新,又省去手动逐个查询的繁琐。

2. 通用扩展函数实现

基础版(无缓存)

封装Realm扩展函数,自动处理带主键对象的复用逻辑:

import io.realm.kotlin.Realm
import io.realm.kotlin.ext.copyToRealm
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

/**
 * 通用函数:获取或创建带主键的RealmObject实例
 * 存在则返回数据库中已有实例,不存在则插入并返回新实例
 */
fun <T : RealmObject> Realm.getOrCreate(obj: T): T {
    // 反射获取主键字段
    val primaryKeyField = obj::class.members.firstOrNull { 
        it.findAnnotation<PrimaryKey>() != null 
    } ?: throw IllegalArgumentException("${obj::class.simpleName}必须标注@PrimaryKey字段")
    
    val primaryKeyValue = primaryKeyField.call(obj) 
        ?: throw IllegalArgumentException("${obj::class.simpleName}的主键值不能为null")
    
    // 查询数据库中是否存在该主键的实例
    val existingInstance = this.query(obj::class, "${primaryKeyField.name} == $0", primaryKeyValue)
        .first()
        .find()
    
    return existingInstance ?: obj.copyToRealm()
}

优化版(带事务内缓存)

如果处理数据量极大,重复查询数据库会有性能损耗,可在写事务内添加内存缓存,避免重复查询:

import io.realm.kotlin.Realm
import io.realm.kotlin.ext.copyToRealm
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

/**
 * 带缓存的通用函数:获取或创建带主键的RealmObject实例
 * 事务内缓存已查询的实例,减少数据库查询次数
 */
@Suppress("UNCHECKED_CAST")
fun <T : RealmObject> Realm.getOrCreateWithCache(obj: T, cache: MutableMap<Pair<Class<*>, Any?>, T>): T {
    val primaryKeyField = obj::class.members.firstOrNull { 
        it.findAnnotation<PrimaryKey>() != null 
    } ?: throw IllegalArgumentException("${obj::class.simpleName}必须标注@PrimaryKey字段")
    
    val primaryKeyValue = primaryKeyField.call(obj) 
        ?: throw IllegalArgumentException("${obj::class.simpleName}的主键值不能为null")
    
    val cacheKey = obj::class.java to primaryKeyValue
    
    // 优先从缓存获取
    cache[cacheKey]?.let { return it }
    
    // 缓存不存在则查询数据库
    val existingInstance = this.query(obj::class, "${primaryKeyField.name} == $0", primaryKeyValue)
        .first()
        .find()
    
    val result = existingInstance ?: obj.copyToRealm()
    cache[cacheKey] = result
    return result
}

3. 使用示例

基础版使用

在写事务中调用扩展函数处理嵌套对象:

realm.write {
    csvDataList.forEach { csvItem ->
        // 处理Quantity对象,自动复用或插入
        val quantity = this@write.getOrCreate(Quantity().apply {
            id = "${csvItem.value}_${csvItem.unit}"
            value = csvItem.value
            unit = csvItem.unit
        })
        
        // 处理Another对象及其嵌套的Price
        val anotherPrice = this@write.getOrCreate(Quantity().apply {
            id = "${csvItem.anotherPriceValue}_${csvItem.anotherPriceUnit}"
            value = csvItem.anotherPriceValue
            unit = csvItem.anotherPriceUnit
        })
        val another = this@write.getOrCreate(Another().apply {
            price = anotherPrice
            // 赋值其他字段
        })
        
        // 创建Example对象,关联复用后的嵌套实例
        val example = Example().apply {
            field1 = csvItem.field1
            anotherRealmObjectRef = quantity
            anotherRealmObjectRef2 = another
        }
        this.copyToRealm(example)
    }
}

优化版使用

在写事务内初始化缓存,减少重复查询:

realm.write {
    // 事务内缓存,键为(对象Class,主键值)
    val cache = mutableMapOf<Pair<Class<*>, Any?>, RealmObject>()
    
    csvDataList.forEach { csvItem ->
        val quantity = this@write.getOrCreateWithCache(
            obj = Quantity().apply {
                id = "${csvItem.value}_${csvItem.unit}"
                value = csvItem.value
                unit = csvItem.unit
            },
            cache = cache as MutableMap<Pair<Class<*>, Any?>, Quantity>
        )
        
        // 其他嵌套对象同理使用缓存处理
        val example = Example().apply {
            field1 = csvItem.field1
            anotherRealmObjectRef = quantity
            // ...
        }
        this.copyToRealm(example)
    }
}

4. 关键优势

  • 无需手动查询:通用函数自动处理所有带主键的嵌套对象,无需为每个对象写单独的查询逻辑。
  • 避免无意义更新:仅插入不存在的实例,不会修改已有实例的字段,减少Realm版本历史占用,缩小数据库体积。
  • 性能优化:带缓存的版本可大幅减少数据库查询次数,提升批量插入效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:20:44