我们在开发或者产品升级的时候经常会遇到托管模型的结构化修改(也就是表的结构改变),那么我们就需要数据迁移到新的模型中。
- 轻量级迁移方式
- 默认的迁移方式
- 迁移管理器迁移方式
现在记录一下最后一种迁移方式,迁移管理器迁移数据。
大家有没有这样的体验,就是升级新版QQ的时候,第一次开启会首先要导入原来数据。 会有导入的进度条。对当数据迁移量很大的时候就需要花费较长的时间。为了更好的用户体验,我们需要让用户知道已经迁移多少,还有多少。这样不至于让用户无目标的等待,或者不耐烦的关闭。 迁移管理器就为我们提供了,迁移进度值。
首先我们先下载操作底板:下载
下载完成后,运行一下程序,插入数据。
如果程序出现崩溃请参考前两篇文章进行处理。(删除原有数据库文件,或者clean一下)。
首先我们添加Model版本
然后设置当前运行版本
然后我们就可以操作新的实体了,可以直接删除,新建一个实体,或者修改现在的实体。
我们删除掉重新创建 Entity2
好创建好新的实体后,我们来创建映射模型。
创建完成后我们来设置一下
完成映射模型
当进行迁移的时候我们需要判断一下是否需要迁移,是否已经迁移过了。
也就是比较原有的存储模型是否与现在的存储模型相同。
-(BOOL)isMigrationNecessaryForStore:(NSURL *)storeURL
{
//是否存在文件,如果不存在直接返回NO
if (![[NSFileManager defaultManager] fileExistsAtPath:[self storeURL].path]) {
return NO;
}
NSError *error = nil;
//比较存错模型的元数据。
NSDictionary *sourceMataData = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL error:&error];
NSManagedObjectModel *destinationModel = _coordinator.managedObjectModel;
if ([destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMataData]) {
return NO;
}
return YES;
}
下面开始数据迁移
-(BOOL)migrateStore:(NSURL *)sourceStore
{
BOOL successs = NO;
NSError *error = nil;
//原来的数据模型的原信息
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
URL:sourceStore
error:&error];
//原数据模型
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata];
//最新版数据模型
NSManagedObjectModel *destinModel = _model;
//数据迁移的映射模型
NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil
forSourceModel:sourceModel
destinationModel:destinModel];
if (mappingModel) {
NSError *error = nil;
//迁移管理器
NSMigrationManager *migrationManager = [[NSMigrationManager alloc]initWithSourceModel:sourceModel
destinationModel:destinModel];
//这里可以注册监听 NSMigrationManager 的 migrationProgress来查看进度
//先把模型存错到Temp.sqlite
NSURL *destinStore = [[self applictaionStoresDirectory] URLByAppendingPathComponent:@"Temp.sqlite"];
successs = [migrationManager migrateStoreFromURL:sourceStore
type:NSSQLiteStoreType
options:nil
withMappingModel:mappingModel
toDestinationURL:destinStore
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:&error];
if (successs) {
//成功后替换掉原来的旧的文件
if ([self replaceStore:sourceStore withStore:destinStore]) {
// 这里移除监听就可以了。
}
}
}
return successs;
}
文件替换
-(BOOL) replaceStore:(NSURL *)old withStore:(NSURL *)new
{
BOOL success = NO;
NSError *error = nil;
if ([[NSFileManager defaultManager] removeItemAtURL:old error:&error]) {
error = nil;
if ([[NSFileManager defaultManager] moveItemAtURL:new toURL:old error:&error]) {
success = YES;
}
}
return success;
}
现在基本已经基本完成。我们在导入数据模型的时候来判断是否需要数据迁移,那么我们需要在CoreDataHelper.m文件中的loadStore方法中添加。
-(void)loadStore
{
if (_store) {
return ;
}
BOOL useMigrationManager = YES;
if (useMigrationManager &&[self isMigrationNecessaryForStore:[self storeURL]]) {
[self migrateStore:[self storeURL]];
}else{
NSError *error;
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption:@(YES),
NSInferMappingModelAutomaticallyOption:@(NO)};
_store =[_coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self storeURL]
options:options
error:&error];
if (!_store) {
abort();
}
}
}
我们来看看是否成功了。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"%@",NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES));
_coreDataHelper = [[CoreDataHelper alloc]init];//初始化
[_coreDataHelper setupCoreData];//设置持久化对象存储
NSManagedObjectContext *context = _coreDataHelper.managedContext;
//
// for (NSInteger i = 0; i<100000; i++) {
// Entity1 *entity = [NSEntityDescription insertNewObjectForEntityForName:@"Entity1" inManagedObjectContext:context];
// entity.name = [NSString stringWithFormat:@"name%ld",i];
// entity.age = @(i);
// }
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Entity2" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = entityDescription;
NSArray *array = [context executeFetchRequest:request error:nil];
for (Entity2 *entity in array) {
NSLog(@"name2:%@,age2:%@",entity.name2,entity.age2);
}
[_coreDataHelper saveContext];
//
return YES;
}
好的,来运行程序 数据迁移成功了。