上一章节中我们学习了objective-c中realm的对应关系,例子中我们涉及到两种模型关系的对应,但是很多实际项目开发中,我们还会涉及到数据库迁移的问题。我们一起来让我们来探索 Realm 中数据库迁移的方案。
数据库迁移大致分为以下几种情况:
1.数据结构迁移
2.数据迁移
3.属性重命名
4.多版本增量迁移
场景一:
-
一 . 数据结构迁移
比如一个MigrationModel的表,表中只有age和name两个字段,这个时候我们需要增加一个fullName的字段,也就是说现在MigrationModel表中变成了age,name,fullName三个字段, 这时候需要用到数据结构迁移。
原来模型
//
// MigrationModel.h
// realm_oc_demo
//
// Created by allison on 2021/1/24.
//
#import <Realm/Realm.h>
NS_ASSUME_NONNULL_BEGIN
@interface MigrationModel : RLMObject
@property NSString *name;
@property int age;
@end
NS_ASSUME_NONNULL_END
创建数据库方法
- (void)viewDidLoad {
[super viewDidLoad];
[self setDefaultRealmForUser:@"zhangsan"];
[self migrationAction];
}
///数据库迁移
- (void)migrationAction {
NSLog(@"数据库路径:%@",[[RLMRealmConfiguration defaultConfiguration]fileURL]);
MigrationModel *model = [[MigrationModel alloc]init];
model.name = @"Allison";
model.age = 18;
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:model];
}];
}
- (void)setDefaultRealmForUser:(NSString *)userName {
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//使用默认的目录,但是使用用户名来替换默认的文件名
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]URLByAppendingPathComponent:userName]URLByAppendingPathExtension:@"realm"];
//将这个配置应用到默认的realm数据库中
[RLMRealmConfiguration setDefaultConfiguration:config];
}
此时运行demo我们发现数据结构如下所示:
01.png新需求:增加一个fullName的字段,需要在这个表中插入一个fullName的字段,这里就数据到数据结构迁移了。
新模型
#import <Realm/Realm.h>
NS_ASSUME_NONNULL_BEGIN
@interface MigrationModel : RLMObject
//新增字段
@property NSString *fullName;
@property NSString *name;
@property int age;
@end
NS_ASSUME_NONNULL_END
此时如果直接运行,就会出现下面的报错:
02.pngTerminating app due to uncaught exception 'RLMException', reason: 'Migration is required due to the following errors:
这个错误的意思就是数据结构发生了变化,所以我们需要加上迁移的逻辑,并且将版本号+1(版本号默认为0),此时我们可以设置为1.
数据迁移代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
ViewController *vc = [[ViewController alloc]init];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
self.window.rootViewController = nav;
[self migrationRealmAction];
return YES;
}
#pragma mark -- <迁移数据库>
- (void)migrationRealmAction {
NSLog(@"数据库路径:%@",[[RLMRealmConfiguration defaultConfiguration]fileURL]);
//1.获取默认配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//2.叠加版本号(要比上一次版本号高)
int newVersion = 1;
config.schemaVersion = newVersion;
//3.迁移步骤
[config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
if (oldSchemaVersion < newVersion) {
NSLog(@"--需要迁移--oldSchemaVersion:%llu migration:%@",oldSchemaVersion,migration);
}
}];
//4.让配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
//5.需要立即迁移
[RLMRealm defaultRealm];
}
再次运行,数据迁移后的效果如下图所示:
03.png至此数据结构迁移到此完成。
场景二:
刚刚情形1中的场景数据迁移仅仅需要5个步骤即可,比较简单,但是有些场景,新增的字段,是需要保留之前老字段的内容的,比如MigrationModel表中新增一个detailInfo详细信息,detailInfo = age + name + "地址信息"。
老模型
@interface MigrationModel : RLMObject
//新增字段
@property NSString *fullName;
@property NSString *name;
@property int age;
@end
新模型
@interface MigrationModel : RLMObject
//新增字段
@property NSString *detailInfo;
@property NSString *fullName;
@end
此时我们detailInfo字段的内容,要从之前的老字段age和name中获得,所以这个时候迁移的时候就需要遍历之前老的字段并取出赋值。
迁移核心代码如下所示:
#pragma mark -- <迁移数据库>
- (void)migrationRealmAction {
NSLog(@"数据库路径:%@",[[RLMRealmConfiguration defaultConfiguration]fileURL]);
//1.获取默认配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
//2.叠加版本号(要比上一次版本号高)
int newVersion = 5;
config.schemaVersion = newVersion;
//3.迁移步骤
[config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
if (oldSchemaVersion < newVersion) {
NSLog(@"--需要迁移--oldSchemaVersion:%llu migration:%@",oldSchemaVersion,migration);
[migration enumerateObjects:@"MigrationModel" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
newObject[@"detailInfo"] = [NSString stringWithFormat:@"%@ %@ 详细地址XXX ",oldObject[@"age"],oldObject[@"name"]];
}];
}
}];
//4.让配置生效
[RLMRealmConfiguration setDefaultConfiguration:config];
//5.需要立即迁移
[RLMRealm defaultRealm];
}
效果如图4所示
04.png场景三:重命名字段
重命名字段与【场景2】类似,不再赘述。
场景四:多版本增量迁移
指的是原来我APP中有1.0的数据库,我现在需要升级到2.0。这里升级还是类似场景2,只不过需要判断版本的型号,分开来处理即可。
【 例:】
版本0 有三个字段@interface MigrationModel : RLMObject @property NSString *firstName;//姓氏 @property NSString *lastName;//名字 @property int age; @end
版本1 有2个字段
fullName = firstName + lastName@interface MigrationModel : RLMObject @property NSString *fullName;//新增字段 @property int age; @end
版本2也 有2个字段
@interface MigrationModel : RLMObject @property NSString *email;//新增字段 @property int age; @end
问:此时如何做迁移
以下是多版本迁移的核心代码//3.迁移步骤 [config setMigrationBlock:^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) { if (oldSchemaVersion < newVersion) { if (oldSchemaVersion < 1) { [migration enumerateObjects:@"MigrationModel" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { newObject[@"detailInfo"] = [NSString stringWithFormat:@"%@ %@",oldObject[@"age"],oldObject[@"name"]]; }]; } if (oldSchemaVersion < 2) { [migration enumerateObjects:@"MigrationModel" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) { newObject[@"email"] = @"xxxx"; }]; } } }];
网友评论