CoreData managedObjectContext save违反Z_PK的UNIQUE约束

时间:2015-03-14 14:44:12

标签: ios sqlite unique datastore

我在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是主键

这里是sqlite"编辑器内的数据表的快速屏幕截图"在Firefox中: datastructure from sqlite

业务对象实体本身的定义不包括Z_PK列:

@implementation ghotiEntity

@dynamic numberCorrect;
@dynamic numberIncorrect;
@dynamic likelihoodOfPresentingWord;
@dynamic englishText;
@dynamic hebrewText;
@dynamic phoneticText;

@end

我感到烦恼 - 但是顺从 - 如果它只是将ghotiEntity.Z_PK设置为数据存储区+ 1中Z_PK的最大值那么简单,但该列甚至不是实体定义的一部分第一名。


编辑 - 一位有用的用户要求提供代码,因此我将其添加到此处,而不是尝试将其添加到评论中:

我正在使用我在网络上看到的核心数据方法的mmBusinessObject集合。 Kevin McNeish完全解释了here

基本结构如下:

mmBusinessObject.m和.h可以很好地将托管对象方法包装成某些内容,并且易于阅读和处理"。为了便于阅读,我已经复制了两个元素[entity createEntities]和[entity saveEntities]的片段以及本文末尾的完整.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行,我得到诊断信息,它是具有UNIQUE CONSTRAINT问题的Z_PK列。


如上所述,这是完整的.m文件。我需要确保很明显,如上所述,此代码的功劳归功于Kevin McNeish,或者在其他地方发布相同代码的其他任何合作者......只需在Google中搜索mmBusinessObject即可。我认为凯文是创始人,他的教程非常好。我见过的版本中的前几行都有Kevin的名字!

#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

3 个答案:

答案 0 :(得分:1)

查看代码,了解为数据库创建对象的所有位置。确保您没有从多个线程创建对象。

答案 1 :(得分:1)

我遇到了与Z_PK约束问题相同的问题。这源于我从应用程序的文档文件夹中复制了空的sqlite数据库并使用Firefox的SQLite管理器加载项预先填充它。我忘记的步骤是更新Z_PrimaryKey表...您必须更新每个表的Z_MAX值,以便Core Data将知道在将新记录添加到数据库时要使用的下一个Z_PK。只需将每个Z_Max值设置为每个表中的记录数。保存并将其放回到您的应用中,一切都会好的。

答案 2 :(得分:0)

我遇到了同样的问题,但是当我想要更新条目时,我有这个问题,所以我用refreshObject修复然后像这样保存:

[dataManager.managedObjectContext refreshObject:object mergeChanges:YES];
[dataManager saveContextWithBlock:block];