2

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

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

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

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

详情(以防万一):

我有两个选择(如建议的那样):

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

对于第一个选项,我有以下担忧:

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

对于第二种选择,我有以下担忧:

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

2 回答 2

0

I don't know if it's really better to hold 2 databases, because you mostly change more than only the one to extend functionalities.

For database migration, you can use something like this.

First use the internal versioning system of the core-data and hold your currently used version in the NSUserDefaults and in my example the infoDictionary (your connected .plist file). So you can first try the lightweight migration and then the manuel if you changes cannot be automaticaly merged.

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

hope I could help you a bit ;)

于 2013-05-13T13:07:00.910 回答
0

我最终使用了 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;
}

参考:

多个(两个)持久存储可以与一个对象模型一起使用,同时保持一个到另一个的关系吗?

于 2013-05-14T13:02:48.967 回答