【问题标题】:Importing Large Data Set into Core-Data (Solution with Multiple Stores)将大数据集导入Core-Data(多存储解决方案)
【发布时间】:2013-05-07 11:40:32
【问题描述】:

我知道这个话题已经讨论过了,但我正在寻找一个已经在任何地方讨论过的特定解决方案,即使用多个存储而不是一个 - 一个用于默认数据,另一个用于用户输入的数据。

在我的应用程序中,我想为实体 X(仅)预加载大量数据,即 20 MB json 文件。该模型还包含实体 Y 和实体 Z。我认为最适合我需要的解决方案如下:

使用两个 sqlite 数据库。仅适用于 Entity X 的一个数据库,可以在应用程序的未来版本中替换...而不影响 Entity Y 和 Entity Z 的数据(毫不费力)。

  • 如何实施这样的解决方案?
  • 实用吗?
  • 我需要制作两个模型吗?
  • 在这种情况下如何设置核心数据托管对象上下文?

详情(以防万一):

我有两个选择(按照建议):

  1. 将 json 文件与应用程序捆绑并在应用程序首次启动时加载
  2. 生成一个 sqlite 数据库,并使用预填充的数据库发布应用程序

对于第一个选项,我有以下顾虑:

  • 应用程序启动会很慢(因为解析一个 20 MB 的文件然后在 core-data 中创建相应的记录需要一些时间)。
  • 一旦使用了文件,就不需要应用程序使用的 20 MB 空间。我不认为有什么办法可以摆脱那个文件,是吗?

对于第二种选择,我有以下顾虑:

  • 假设在我的应用程序 2.0 版中,我想更新默认存储。例如,我在 1.0 版中提供的默认 sqlite 数据库有一些问题,或者我想添加更多“默认”记录?我有多个实体,用户可能已将数据添加到其他实体。所以,我不能替换 sqlite 数据库。那么,我该如何更新数据库中的记录呢?

【问题讨论】:

  • 根据您的解释,在一个持久存储中使用多个实体与在 2 个存储和多个实体中使用会有什么问题?我看不出为什么你不能只用多个实体来完成同样的事情。这样,您只需将所有数据预加载到一个实体中,然后在完成后删除该实体中的所有对象。
  • 顺便说一句,要删除文件,请尝试 [[NSFileManager defaultManager] removeItemAtURL:myLocalURL error:&error];
  • 好的,我可以为实体 X 预加载一个 sqlite 数据库,并将它与我的应用程序 v1.0 一起发布。现在,当我必须更改一些条目时我该怎么办......并在 v2.0 中更新实体 X 的默认数据库?
  • 我无法从我的包中删除文件,可以吗?
  • 如果您的 json 文件是项目结构中的一个简单文件,您应该可以使用 NSFileManager (developer.apple.com/library/ios/#documentation/Cocoa/Reference/…) 将其删除

标签: ios json core-data import


【解决方案1】:

我最终使用了 2 个持久存储。一个用于“readwrite”数据库,一个用于“readonly”(种子)数据库。在应用程序的未来版本中,我可以毫无问题地发布更新的种子数据库。

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    // Define the Core Data version migration options
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
                             nil];

    // Attempt to load the persistent store
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

    // Create the default/ user model persistent store
    {
        NSString *storeFileName = ...;
        NSString *configuration = @"Readwite";
        NSURL *storeURL = [[self applicationLocalDatabaseDirectory] URLByAppendingPathComponent:storeFileName];

        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                       configuration:configuration
                                                                 URL:storeURL
                                                             options:options
                                                               error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    // Create the seed data persistent store
    {
        NSURL *seedDataURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"SeedData" ofType:@"sqlite"]];
        NSString *configuration = @"SeedData";

        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                       configuration:configuration
                                                                 URL:seedDataURL
                                                             options:options
                                                               error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort();
            abort();
        }
    }

    // Developer's Note:
    // Instead of creating a single store, we created two persistent stores in an almost identical manner.
    // The only difference is that we explicitly named the configuration for the store to use, instead of
    // passing nil for the configuration parameter. Note that the configurations were set in xcdatamodeld file.

    return _persistentStoreCoordinator;
}

参考:

Can multiple (two) persistent stores be used with one object model, while maintaining relations from one to the other?

【讨论】:

    【解决方案2】:

    我不知道保存 2 个数据库是否真的更好,因为您通常会更改不止一个以扩展功能。

    对于数据库迁移,您可以使用类似的东西。

    首先使用核心数据的内部版本控制系统,并将您当前使用的版本保存在NSUserDefaults 中,在我的示例中为infoDictionary(您连接的.plist 文件)。所以你可以先尝试轻量级的迁移,如果你的改动不能自动合并,可以试试manuel。

    NSNumber *newDbVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"DBVersion"];
    NSNumber *oldDbVersion = [[NSUserDefaults standardUserDefaults] objectForKey:@"DBVersion"];
    
    NSURL *storeUrlOld;
    if(!oldDbVersion)
        storeUrlOld = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"default.sqlite"]]; // default path
    else
        storeUrlOld = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: [NSString stringWithFormat:@"db_%d.sqlite",[oldDbVersion intValue]]]];
    
    NSURL *storeUrlNew = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: [NSString stringWithFormat:@"db_%d.sqlite",[newDbVersion intValue]]]];
    
    NSString *path = [[NSBundle mainBundle] pathForResource:@"myDB" ofType:@"momd"];
    if(oldDbVersion)
        path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"db_%d.mom", [oldDbVersion intValue]]];
    
    NSURL *momURLOld = [NSURL fileURLWithPath:path];
    NSLog(@"mom-path old: %@", path);
    NSLog(@"mom-url old: %@", momURLOld);
    
    NSManagedObjectModel *oldManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURLOld];
    
    // - - -
    
    path = nil;
    path = [[NSBundle mainBundle] pathForResource:@"db" ofType:@"momd"];
    path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"db_%d.mom",[newDbVersion intValue]]];
    
    NSURL *momURLNew = [NSURL fileURLWithPath:path];
    NSLog(@"mom-path new: %@", path);
    NSLog(@"mom-url new: %@", momURLNew);
    NSManagedObjectModel *newManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURLNew];
    
    // # # # # # # # # # # # # # # # # # # # # #
    // - - - - Connect with old Database - - - -
    // # # # # # # # # # # # # # # # # # # # # #
    
    NSError *error;
    NSPersistentStoreCoordinator *persistentStoreCoordinatorOld = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:oldManagedObjectModel];
    
    // Allow inferred migration from the original version of the application.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
    
    if (![persistentStoreCoordinatorOld addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrlOld options:options error:&error]) 
    {
        // Handle the error
        NSLog(@"Failed to add old persistent store: %@", [error localizedDescription]);
        NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    
        if(detailedErrors != nil && [detailedErrors count] > 0) 
        {
            for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
            }
        }
        else 
        {
            NSLog(@"ERROR persistentStoreCoordinator: %@", [error userInfo]);
        }
        return;
    }
    
    NSManagedObjectContext *oldManagedObjectContext = [[NSManagedObjectContext alloc] init];
    [oldManagedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinatorOld];
    
    // # # # # # # # # # # # # # # # # # # # # #
    // - - - - Connect with new Database - - - -
    // # # # # # # # # # # # # # # # # # # # # #
    
    NSPersistentStoreCoordinator *persistentStoreCoordinatorNew = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: newManagedObjectModel];
    
    if (![persistentStoreCoordinatorNew addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrlNew options:options error:&error]) 
    {
        // Handle the error
        NSLog(@"Failed to add new persistent store: %@", [error localizedDescription]);
        NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    
        if(detailedErrors != nil && [detailedErrors count] > 0) 
        {
            for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
            }
        }
        else 
        {
            NSLog(@"ERROR persistentStoreCoordinator: %@", [error userInfo]);
        }
        return;
    }    
    
    NSManagedObjectContext *newManagedObjectContext = [[NSManagedObjectContext alloc] init];
    [newManagedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinatorNew];
    managedObjectContext = newManagedObjectContext;
    
    // # # # # # # # # # # # # # # # # # # # # # # #
    // - - - Transfere data from old DB to new - - -
    // # # # # # # # # # # # # # # # # # # # # # # #
    
    // - - -
    
    // # # # # # # # # #
    // - - - Users - - -
    // # # # # # # # # # 
    
    NSString *entityName = @"User";
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:oldManagedObjectContext];
    [request setEntity:entity];
    NSString *predicateFormat ;
    NSPredicate *predicate;
    error = nil;
    
    NSMutableArray *mutableFetchResultsUsers = [NSMutableArray arrayWithArray: [oldManagedObjectContext executeFetchRequest:request error:&error]];
    
    if (mutableFetchResultsUsers == nil) {
        // Handle the error.
    }
    NSLog(@"Users: %@", mutableFetchResultsUsers);
    for(User *user in mutableFetchResultsUsers)
    {
        NSLog(@"%@, %@, %@",user.userLogin,user.userDomain, user.serverAddress);
        User *userNew = [[DatabaseFactory sharedInstance] newObject:@"User"];
    [...] // do here integration
        userNew.attibute = user.attribute;
    
        [self saveContext];
    }
    [request release];
    request = nil;
    
    // next one
    

    希望我能帮到你一点;)

    【讨论】:

      猜你喜欢
      • 2014-06-25
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-26
      相关资源
      最近更新 更多