【发布时间】:2015-05-16 23:52:31
【问题描述】:
我在 iOS 应用程序中使用 CoreData 来管理“闪存卡”应用程序中的单词,供人们学习新语言。
我遇到的问题是,当我为新实体设置数据并尝试将其保存到数据存储区时,我违反了 sqlite 数据库上的 UNIQUE CONSTRAINT 要求。有问题的列是 Z_PK 列,我理解它是最初创建数据存储时由 iOS 核心数据方法创建的 PRIMARY KEY。
这是我尝试保存时收到的 UNIQUE CONSTRAINT 消息:
2015-03-14 09:25:14.427 ghoti[25856:1107373] CoreData:错误:(1555) UNIQUE 约束失败:ZGHOTIENTITY.Z_PK (lldb)
Z 是所有这些 SQL 列上的前缀
GHOTIENTITY 是我的数据存储
Z_PK 为主键
以下是 Firefox 中 sqlite“编辑器”中数据表的快速截图:
业务对象实体本身的定义不包括Z_PK列:
@implementation ghotiEntity
@dynamic numberCorrect;
@dynamic numberIncorrect;
@dynamic likelihoodOfPresentingWord;
@dynamic englishText;
@dynamic hebrewText;
@dynamic phoneticText;
@end
我会很生气 - 但符合要求 - 如果它只是将 ghotiEntity.Z_PK 设置为数据存储区中 Z_PK 的最大值 + 1 一样简单,但该列甚至不是第一个实体定义的一部分地点。
编辑 - 一位乐于助人的用户要求提供代码,因此我将其添加到此处,而不是尝试将其填充到评论中:
我正在使用我在网络上看到的核心数据方法的 mmBusinessObject 集合。 Kevin McNeish 充分解释了它here。
基本结构是这样的:
mmBusinessObject.m 和 .h 在将 managedObject 方法包装成“易于阅读和处理”的东西方面做得很好。为了便于阅读,我在此处复制了两个元素 [entity createEntities] 和 [entity saveEntities] 的 sn-p,并在本文末尾复制了完整的 .m 文件。 (请注意,我还没有内置所有的错误检查,我只是想让基本功能正常工作。
创建一个新实体:
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
保存更改
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
所以我的操作代码(在我的 ViewController.m 文件中)是这样做的:(注意 ghotiEntity 是我的特定业务对象。
- (IBAction)saveButtonAction:(UIButton *)sender {
ghotiEntity *newGhotiEntry = (ghotiEntity *)[ghotiData createEntity];
newGhotiEntry.englishText=self.englishWord.text;
newGhotiEntry.hebrewText=self.hebrewWord.text;
newGhotiEntry.phoneticText=self.phoneticWord.text;
newGhotiEntry.numberCorrect=0;
newGhotiEntry.numberIncorrect=0;
newGhotiEntry.likelihoodOfPresentingWord=0;
[ghotiData saveEntities];
[self enableRegularUI];
[self pickNextCard];
}
我从上面 saveEntities 方法中的 NSLog 行得到诊断信息,即 Z_PK 列存在 UNIQUE CONSTRAINT 问题。
如前所述,这里是完整的 .m 文件。我需要确保很明显,这段代码的功劳归于上面提到的 Kevin McNeish,或者在其他地方发布了相同代码的其他几个合作者中的任何一个......只需在谷歌中搜索 mmBusinessObject。我认为凯文是创始人,他的教程非常好。我见过的版本的前几行都有凯文的名字!
#import "mmBusinessObject.h"
@implementation mmBusinessObject
// Initialization
- (id)init
{
if ((self = [super init])) {
_copyDatabaseIfNotPresent = YES;
}
return self;
}
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
// Delete the specified entity
- (void) deleteEntity:(NSManagedObject *)entity {
[self.managedObjectContext deleteObject:entity];
}
// Gets entities for the specified request
- (NSMutableArray *)getEntities: (NSString *)entityName sortedBy:(NSSortDescriptor *)sortDescriptor matchingPredicate:(NSPredicate *)predicate
{
NSError *error = nil;
// Create the request object
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Set the entity type to be fetched
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Set the predicate if specified
if (predicate) {
[request setPredicate:predicate];
}
// Set the sort descriptor if specified
if (sortDescriptor) {
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Execute the fetch
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
return mutableFetchResults;
}
// Gets all entities of the default type
- (NSMutableArray *)getAllEntities
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:nil];
}
// Gets entities of the default type matching the predicate
- (NSMutableArray *)getEntitiesMatchingPredicate: (NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Gets entities of the default type matching the predicate string
- (NSMutableArray *)getEntitiesMatchingPredicateString: (NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Get entities of the default type sorted by descriptor matching the predicate
- (NSMutableArray *)getEntitiesSortedBy: (NSSortDescriptor *) sortDescriptor
matchingPredicate:(NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:sortDescriptor matchingPredicate:predicate];
}
// Gets entities of the specified type sorted by descriptor, and matching the predicate string
- (NSMutableArray *)getEntities: (NSString *)entityName
sortedBy:(NSSortDescriptor *)sortDescriptor
matchingPredicateString:(NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:entityName sortedBy:sortDescriptor matchingPredicate:predicate];
}
- (void) registerRelatedObject:(mmBusinessObject *)controllerObject
{
controllerObject.managedObjectContext = self.managedObjectContext;
}
// Saves all changes (insert, update, delete) of entities
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
#pragma mark -
#pragma mark Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created from the application's model.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:self.dbName withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// If the sqlite database doesn't already exist, create it
// by copying the sqlite database included in this project
if (self.copyDatabaseIfNotPresent) {
// Get the documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = paths[0];
// Append the name of the database to get the full path
NSString *dbcPath = [docsDir stringByAppendingPathComponent:
[self.dbName stringByAppendingString:@".sqlite"]];
// Create database if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbcPath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:self.dbName ofType:@"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:dbcPath error:NULL];
}
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:
[NSString stringWithFormat:@"%@%@", self.dbName, @".sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(@"%@",storeURL);
if ([error code] == 134100) {
[self performAutomaticLightweightMigration];
}
else
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
return _persistentStoreCoordinator;
}
- (void)performAutomaticLightweightMigration {
NSError *error;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@%@", self.dbName, @".sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error]){
// Handle the error.
}
}
#pragma mark -
#pragma mark Application's Documents directory
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
@end
【问题讨论】:
-
请提供您如何创建和保存 ghoti 对象的代码。 CoreData 在内部管理私钥,如果一切正常,它通常可以完美运行。
-
感谢您的评论。我已经编辑了原始帖子以包含更多代码和上下文。
-
在实施 mmBusinessObject 时,如果项目中存在数据库,则会从项目中复制该数据库。您的项目中有预生成的 .sqlite 数据库吗?
-
是的,有一个现有的 sqlite 数据库。我知道它正在拾取它,因为当我读取表 getAllEntities 时,表的所有行都按预期传递到我的 NSMutableArray 中。
标签: ios sqlite unique datastore