核心数据 - 使用 NSBatchInsertRequest 设置多对一关系

Posted

技术标签:

【中文标题】核心数据 - 使用 NSBatchInsertRequest 设置多对一关系【英文标题】:Core data - Set many-to-one relationship with NSBatchInsertRequest 【发布时间】:2020-12-31 17:11:17 【问题描述】:

我有两个实体,UserPost。一个User 包含许多Post,而每个Post 只有一个User

我正在尝试批量插入超过 1000 个帖子。

private func newBatchInsertRequest(with posts: [PostData]) -> NSBatchInsertRequest 
    var index = 0
    let total = posts.count
    let batchInsert = NSBatchInsertRequest(entity: Post.entity())  (managedObject: NSManagedObject) -> Bool in
        guard index < total else  return true 
        
        if let post = managedObject as? Post 
            let data = posts[index]
            post.createdData = data.createdDate
            post.identifier = data.identifier
            post.text = data.text
        
        
        index += 1
        return false
    
    
    PersistenceController.shared.container.performBackgroundTask  context in
        try? context.execute(batchInsert)
        try? context.save()
    

它会插入我想要插入的所有帖子。但是,我无法配置如何设置他们的User

我尝试使用以下代码,但没有成功。

let updateRequest = NSBatchUpdateRequest(entity: Post.entity())
updateRequest.resultType = .updatedObjectIDsResultType
updateRequest.propertiesToUpdate = ["user": user]

 try? context.execute(updateRequest) 

我收到以下错误。

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid relationship ((<NSRelationshipDescription: 0x2801045a0>), name item, isOptional 1, isTransient 0, entity Post, renamingIdentifier item

我可以一一设置他们的用户,但是处理时间长,效率低。

如何更高效地更新 Posts 的用户属性?


更新

所有这些帖子都属于一个User,它还不存在。我需要在执行NSBatchInsertRequest之前或之后创建它。

User 具有三个属性

   1. createdDate: Date
   2. identifier: UUID
   3. name: String

我的目标是使用NSBatchInsertRequest 或在私有上下文中插入属于一个使用的帖子,这样它就不会阻塞主线程。

【问题讨论】:

不清楚您如何确定应该将哪个用户连接到帖子? 那为什么不先创建用户,然后分配给newBatchInsertRequest中的帖子呢? 他们有不同的上下文。在核心数据中,绑定到一个上下文的对象不能在另一个上下文中使用。 @JoakimDanielson 【参考方案1】:

这是我会尝试的:

在批处理操作之前创建User。是的,它将在不同的环境中——只要确保你抓住它的NSManagedObjectID(它是NSManagedObject 上的一个属性)并将它保存在一个私有属性上。 在您的PersistenceController.shared.container.performBackgroundTask 中,您可以引用您的私人上下文。使用之前的托管对象 ID 在您的私有上下文中获取/注册相同的 User
let user = context.object(with: userManagedObjectID)
然后将此用户传递给创建批量插入请求的方法,以设置 Post 对象的用户属性。

我相信,如果您在数据模型编辑器中设置了反向关系,则无需填充 User.posts。这应该会为您自动发生。

【讨论】:

谢谢。这没用。我在PersistenceController.shared.container.performBackgroundTask 之前创建了一个用户并将context.object(with: userManagedObjectID) 分配给post.user。得到这个错误:'非法尝试在不同上下文中的对象之间建立关系'u'ser【参考方案2】:

我以不同的方式解决了这个问题。

    在我的实体中需要添加关系占位符属性的 tmpID。 创建批量插入时,将 tmpID 设置为特定字符串。例如父对象ID parent.objectID。 正在执行批量插入。 然后使用 tmpID 从 CoreData 获取所有对象。 然后扔掉所有人并设置:
      关系 tmpID 为 nil,不留内存。
    保存托管对象上下文。

【讨论】:

以上是关于核心数据 - 使用 NSBatchInsertRequest 设置多对一关系的主要内容,如果未能解决你的问题,请参考以下文章

更改核心数据堆栈后使用核心数据创建 SQLite 文件后未更新

从核心数据同步数据

核心数据,使用啥并发模型?

使用核心数据时出错

使用 JSON 键值 ( id ) 更新核心数据

如何使用tableview删除核心数据中的数据