Core Data의 createOrUpdate는 병합 정책에 따라 달라집니다.
소개
타이틀을 고민했지만 적절한 타이틀의 표현이 어렵다. Core Data에는 createOrUpdate는 존재하지 않지만 비슷한 것을 시도한 경우에 대해.
먼저 Constraints를 id로 설정하고 Constraints에 따라 context.save를 실행하는 경우를 가정합니다. 그 때 병합 정책에 의해 갱신되거나 되지 않는 것이 정해진다.
createOrUpdate 예제
name 속성으로 설정하지 않는 경우
병합 정책 NSMergeByPropertyObjectTrumpMergePolicy
병합 정책을 NSMergeByPropertyObjectTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
@objc(Person)
public class Person: NSManagedObject {
@NSManaged public var id: Int32
@NSManaged public var name: String?
}
func createPerson() {
func debug(_ person: Person) {
print("---")
print("person.id:", person.id)
print("person.name:", person.name ?? "nil")
}
let context = persistentContainer.viewContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // このポリシーによって変わる
do {
let person = Person(context: context)
person.id = 1
person.name = "p1"
saveContext()
let request = NSFetchRequest<Person>(entityName: "Person")
let entities = try! context.fetch(request)
debug(entities.first!)
}
do {
let person = Person(context: context)
person.id = 1
// person.name = "p1" // ここをセットしない
saveContext()
let request = NSFetchRequest<Person>(entityName: "Person")
let entities = try! context.fetch(request)
debug(entities.first!)
}
}
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
출력은 아래와 같다. name 을 세트 하지 않으면 nil 에 의해 덧쓰기된다 (또는 새로운 오브젝트가 id: 1 로서 덧쓰기된다).
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: nil
병합 정책 NSMergeByPropertyStoreTrumpMergePolicy
병합 정책을 NSMergeByPropertyStoreTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: p1
아마도 영속화된 데이터와 id에 의한 충돌이 일어나 병합이 일어나고 있다. 그 때 Store를 우선하기 위해 p1을 덮어 쓰지 않는다.
Store를 우선하기 위하여 name가 다시 쓰여지면 이미 p1이 아닐 수도 있다.
병합 정책이 NSErrorMergePolicy인 경우
병합 정책을 NSErrorMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=(
"NSConstraintConflict (0x600000af8b80) for constraint (\n id\n): database: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>, conflictedObjects: (\n \"0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>\"\n)"
)}, ["NSExceptionOmitCallstacks": 1, "conflictList": <__NSArrayM 0x6000011ef3c0>(
NSConstraintConflict (0x600000af8b80) for constraint (
id
): database: <Person: 0x600003c84d20> (entity: Person; id: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>; data: {
id = 1;
name = p1;
}), conflictedObjects: (
"<Person: 0x600003c9c730> (entity: Person; id: 0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>; data: {\n id = 1;\n name = nil;\n})"
)
)
]
처음에는 작성되었지만 다음에 같은 id로 갱신하려고 하면 충돌이 일어나 병합이 실패한다.
결론
Core Data의 충돌은 복수 context의 동시 병합에 기인하는 것이 아니라, 이미 영속화되고 있는 Store와 온메모리의 데이터의 불일치에 의해 충돌이 일어나 발생한다. 동시 병합에서도 결국 충돌 발생 위험은 높아지지만 그것은 계기의 하나 밖에 없다. 충돌시 병합 정책에 따라 행동이 결정된다는 것입니다.
병합 정책의 기본값은 NSErrorMergePolicy이기 때문에 상대적으로 실수를 알기 쉽습니다.
관련
Realm은 어떻게 될 것인가?
Reference
이 문제에 관하여(Core Data의 createOrUpdate는 병합 정책에 따라 달라집니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/yimajo/items/426c54062cf0101f69bf
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
name 속성으로 설정하지 않는 경우
병합 정책 NSMergeByPropertyObjectTrumpMergePolicy
병합 정책을 NSMergeByPropertyObjectTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
@objc(Person)
public class Person: NSManagedObject {
@NSManaged public var id: Int32
@NSManaged public var name: String?
}
func createPerson() {
func debug(_ person: Person) {
print("---")
print("person.id:", person.id)
print("person.name:", person.name ?? "nil")
}
let context = persistentContainer.viewContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // このポリシーによって変わる
do {
let person = Person(context: context)
person.id = 1
person.name = "p1"
saveContext()
let request = NSFetchRequest<Person>(entityName: "Person")
let entities = try! context.fetch(request)
debug(entities.first!)
}
do {
let person = Person(context: context)
person.id = 1
// person.name = "p1" // ここをセットしない
saveContext()
let request = NSFetchRequest<Person>(entityName: "Person")
let entities = try! context.fetch(request)
debug(entities.first!)
}
}
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
출력은 아래와 같다. name 을 세트 하지 않으면 nil 에 의해 덧쓰기된다 (또는 새로운 오브젝트가 id: 1 로서 덧쓰기된다).
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: nil
병합 정책 NSMergeByPropertyStoreTrumpMergePolicy
병합 정책을 NSMergeByPropertyStoreTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: p1
아마도 영속화된 데이터와 id에 의한 충돌이 일어나 병합이 일어나고 있다. 그 때 Store를 우선하기 위해 p1을 덮어 쓰지 않는다.
Store를 우선하기 위하여 name가 다시 쓰여지면 이미 p1이 아닐 수도 있다.
병합 정책이 NSErrorMergePolicy인 경우
병합 정책을 NSErrorMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=(
"NSConstraintConflict (0x600000af8b80) for constraint (\n id\n): database: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>, conflictedObjects: (\n \"0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>\"\n)"
)}, ["NSExceptionOmitCallstacks": 1, "conflictList": <__NSArrayM 0x6000011ef3c0>(
NSConstraintConflict (0x600000af8b80) for constraint (
id
): database: <Person: 0x600003c84d20> (entity: Person; id: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>; data: {
id = 1;
name = p1;
}), conflictedObjects: (
"<Person: 0x600003c9c730> (entity: Person; id: 0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>; data: {\n id = 1;\n name = nil;\n})"
)
)
]
처음에는 작성되었지만 다음에 같은 id로 갱신하려고 하면 충돌이 일어나 병합이 실패한다.
결론
Core Data의 충돌은 복수 context의 동시 병합에 기인하는 것이 아니라, 이미 영속화되고 있는 Store와 온메모리의 데이터의 불일치에 의해 충돌이 일어나 발생한다. 동시 병합에서도 결국 충돌 발생 위험은 높아지지만 그것은 계기의 하나 밖에 없다. 충돌시 병합 정책에 따라 행동이 결정된다는 것입니다.
병합 정책의 기본값은 NSErrorMergePolicy이기 때문에 상대적으로 실수를 알기 쉽습니다.
관련
Realm은 어떻게 될 것인가?
Reference
이 문제에 관하여(Core Data의 createOrUpdate는 병합 정책에 따라 달라집니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/yimajo/items/426c54062cf0101f69bf
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Realm은 어떻게 될 것인가?
Reference
이 문제에 관하여(Core Data의 createOrUpdate는 병합 정책에 따라 달라집니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/yimajo/items/426c54062cf0101f69bf텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)