Realm数据库

在以前的项目中数据库是使用的FMDB,数据字段在工程中是用OC对象进行引用的,如果要添加字段需要在数据库、OC对象两个地方添加,不是很方便,既然数据是以OC对象展示的,那为何不能通过修改OC对象达到修改数据库字段的目的呢?由于见识有限,当时也没有细想如何达到这样的目的,在时隔一年之后在看了Realm,发现他就是用这样的方式进行数据库操作的,而且看了他的API,还是比较简单的,顺带手的就学习了,现记录如下。

一、Realm数据库的获取

realm提供了三种创建数据库的方法,如下所示

1. 使用默认的数据库

realm为使用者提供了一个默认的数据库,defaultRealm。通过调用 [RLMRealm defaultRealm] 来初始化以及访问我们的 realm 变量。

2.自己定义数据库

2.1使用RLMRealmConfiguration提供的弄人数据库位置并设置数据库的名字、readonly属性等等

1
2
3
4
5
6
7
8
9
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

// 使用默认的目录,但是使用用户名来替换默认的文件名
config.path = [[[config.path stringByDeletingLastPathComponent]
stringByAppendingPathComponent:yourDataBaseName]
stringByAppendingPathExtension:@"realm"];

// 将这个配置应用到默认的 Realm 数据库当中
[RLMRealmConfiguration setDefaultConfiguration:config];

Note:这里在进行配置的时候还有一个可选方法,传递config以及error,你应该一直传入error以捕获由于内存爆掉、设置错误等报错信息的出现

2.2不使用RLMRealmConfiguration提供的path

1
2
3
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dbPath = [docPath stringByAppendingPathComponent:@"db/yourDataBaseName.realm"];
RLMRealm *realm = [RLMRealm realmWithPath:dbPath readOnly:YES error:nil];

3.创建一个内存数据库

一般的realm数据库是存储在硬盘上的,但有时候也需要在内存中创建一个数据库,可以使用如下方法进行创建。

1
2
3
4
5
6
7
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.inMemoryIdentifier = @"yourInMemoryDataBase";
RLMRealm *realm = [RLMRealm realmWithConfiguration:config];

or

RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"yourInMemoryDataBase"];// less is more

Note:内存数据库在每次程序退出时不会保存数据。如果某个内存Realm实例没有被引用,所有的数据在实例对象释放的适合也会被释放。建议你在app中用强引用来钳制所有新建的内存Realm数据库实例。

二、Realm数据库的数据库迁移以及更新

当数据库版本有改动的时候,在以往的数据库管理方法中是将数据库中的字段进行修改,然后才能进行新版本的数据库的使用。而realm的数据库模型是用OC对来定义的数据库,所以在修改数据库的时候只进行realm对象的修改就能够达到目的,这是很方便的。

现在假设有一个realm模型

1
2
3
4
5
6
//v0.0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

在v0.1版本中由于逻辑的改变,需要将firstName以及lastName合并成一个fullName字段,在模型中我们只需要添加如下一个属性即可,如下

1
2
3
4
5
//v0.1
@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end

如果在v0.2版本中又新增加一个属性email

1
2
3
4
5
6
// v0.2
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email; // 新属性
@property int age;
@end

但是这时候realm数据库中的字段还是没有修改的,我们要做的是通知数据库,某些字段和某些字段发生了某些变化,如果在这之前你尝试更新之前就保存的数据的话,数据库就会报错,因为数据库中没有fullName字段。这时候就需要使用数据库迁移。

realm提供了RLMRealmConfiguration的schemaVersionNSInteger属性以及migrationBlockblock属性定义一个迁移操作以及与之关联的架构版本。 每当通过配置创建完一个 RLMRealm之后,迁移闭包将会在迁移需要的时候,将给定的架构版本应用到更新 RLMRealm 操作中。

ok,下面进行迁移的操作,下面是一个最简单的迁移操作模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 设置新的架构版本。这个版本号必须高于之前所用的版本号(如果您之前从未设置过架构版本,那么这个版本号设置为 0)
config.schemaVersion = 1;

// 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
}
};

// 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
[RLMRealmConfiguration setDefaultConfiguration:config];

// 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
[RLMRealm defaultRealm];

进行v0.1版本的数据库更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// enumerateObjects:block: 方法遍历了存储在 Realm 文件中的每一个“Person”对象
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {

// 将名字进行合并,存放在 fullName 域中
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}];
}
};
[RLMRealmConfiguration setDefaultConfiguration:config];

在0.2版本中的迁移逻辑大概就是这样,记得先设置好schemaVersion为当前版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// enumerateObjects:block: 遍历了存储在 Realm 文件中的每一个“Person”对象
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
// 只有当 Realm 数据库的架构版本为 0 的时候,才添加 “fullName” 属性
if (oldSchemaVersion < 1) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}

// 只有当 Realm 数据库的架构版本小于0.2的时候,才添加“email”属性
if (oldSchemaVersion < 2) {
newObject[@"email"] = @"";
}
}];
};
[RLMRealmConfiguration setDefaultConfiguration:config];

// 现在我们已经成功更新了架构版本并且提供了迁移闭包,打开旧有的 Realm 数据库会自动执行此数据迁移,然后成功进行访问
[RLMRealm defaultRealm];