美文网首页
Realm的简单使用

Realm的简单使用

作者: 偏偏就是祢 | 来源:发表于2017-03-22 09:56 被阅读1024次

    前言

    由于最近项目中在用Realm,所以把自己实践过程中的一些心得总结分享一下。

    Realm是由Y Combinator孵化的创业团队开源出来的一款可以用于iOS(同样适用于Swift&Objective-C)和Android的跨平台移动数据库。目前最新版是Realm 2.0.2,支持的平台包括Java,Objective-C,Swift,React Native,Xamarin。

    Realm官网上说了好多优点,我觉得选用Realm的最吸引人的优点就三点:

    跨平台:现在很多应用都是要兼顾iOS和Android两个平台同时开发。如果两个平台都能使用相同的数据库,那就不用考虑内部数据的架构不同,使用Realm提供的API,可以使数据持久化层在两个平台上无差异化的转换。

    简单易用:Core Data 和 SQLite 冗余、繁杂的知识和代码足以吓退绝大多数刚入门的开发者,而换用 Realm,则可以极大地减少学习成本,立即学会本地化存储的方法。毫不吹嘘的说,把官方最新文档完整看一遍,就完全可以上手开发了。

    可视化:Realm 还提供了一个轻量级的数据库查看工具,在Mac Appstore 可以下载“Realm Browser”这个工具,开发者可以查看数据库当中的内容,执行简单的插入和删除数据的操作。毕竟,很多时候,开发者使用数据库的理由是因为要提供一些所谓的“知识库”。

    Realm Browser

    “Realm Browser”这个工具调试起Realm数据库实在太好用了,强烈推荐。

    [RLMRealmConfiguration defaultConfiguration].fileURL

    打印出Realm 数据库地址,然后在Finder中⌘⇧G跳转到对应路径下,用Realm Browser打开对应的.realm文件就可以看到数据啦.

    如果是使用真机调试的话“Xcode->Window->Devices(⌘⇧2)”,然后找到对应的设备与项目,点击Download Container,导出xcappdata文件后,显示包内容,进到AppData->Documents,使用Realm Browser打开.realm文件即可.

    自2012年起, Realm 就已经开始被用于正式的商业产品中了。经过4年的使用,逐步趋于稳定。

    Realm 安装

    使用 Realm 构建应用的基本要求:

    1. iOS 7 及其以上版本, macOS 10.9 及其以上版本,此外 Realm 支持 tvOS 和 watchOS 的所有版本。
    2. 需要使用 Xcode 7.3 或者以后的版本。

    注意: 这里如果是纯的OC项目,就安装OC的Realm,如果是纯的Swift项目,就安装Swift的Realm。如果是混编项目,就需要安装OC的Realm,然后要把 Swift/RLMSupport.swift 文件一同编译进去。

    RLMSupport.swift这个文件为 Objective-C 版本的 Realm 集合类型中引入了 Sequence 一致性,并且重新暴露了一些不能够从 Swift 中进行原生访问的 Objective-C 方法,例如可变参数 (variadic arguments)。更加详细的说明见官方文档

    推荐的安装方法:

    1. CocoaPods,在项目的Podfile中,添加pod 'Realm',在终端运行pod install。
    2. Static Framework
    • 下载 Realm 的最新版本并解压,将 Realm.framework 从 ios/static/文件夹拖曳到您 Xcode 项目中的文件导航器当中。确保 Copy items if needed 选中然后单击 Finish;
    • 在 Xcode 文件导航器中选择您的项目,然后选择您的应用目标,进入到 Build Phases 选项卡中。在 Link Binary with Libraries 中单击 + 号然后添加libc++.dylib;

    Realm 中的相关术语

    为了能更好的理解Realm的使用,先介绍一下涉及到的相关术语。

    RLMRealm:Realm是框架的核心所在,是我们构建数据库的访问点,就如同Core Data的管理对象上下文(managed object context)一样。出于简单起见,realm提供了一个默认的defaultRealm( )的便利构造器方法。

    RLMObject:这是我们自定义的Realm数据模型。创建数据模型的行为对应的就是数据库的结构。要创建一个数据模型,我们只需要继承RLMObject,然后设计我们想要存储的属性即可。

    关系(Relationships):通过简单地在数据模型中声明一个RLMObject类型的属性,我们就可以创建一个“一对多”的对象关系。同样地,我们还可以创建“多对一”和“多对多”的关系。

    写操作事务(Write Transactions):数据库中的所有操作,比如创建、编辑,或者删除对象,都必须在事务中完成。“事务”是指位于write闭包内的代码段。

    查询(Queries):要在数据库中检索信息,我们需要用到“检索”操作。检索最简单的形式是对Realm( )数据库发送查询消息。如果需要检索更复杂的数据,那么还可以使用断言(predicates)、复合查询以及结果排序等等操作。

    RLMResults:这个类是执行任何查询请求后所返回的类,其中包含了一系列的RLMObject对象。RLMResults和NSArray类似,我们可以用下标语法来对其进行访问,并且还可以决定它们之间的关系。不仅如此,它还拥有许多更强大的功能,包括排序、查找等等操作。

    Realm如何使用

    由于Realm的API极为友好,一看就懂,所以这里就按照平时开发的顺序,把需要用到的都梳理一遍。

    1. 创建数据库
    • 一般地,我们使用的为默认的Realm数据库,即调用[RLMRealm defaultRealm]来初始化以及访问我们的realm变量。这个方法将会返回一个 RLMRealm对象,并指向您应用的 Documents (iOS) 文件夹下的一个名为“default.realm”的文件。

    • 用自己创建的数据库

    -(void)creatDataBaseWithName:(NSString *)databaseName {
        NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *path = [docPath objectAtIndex:0];
        NSString *filePath = [path stringByAppendingPathComponent:databaseName];
        NSLog(@"数据库目录 = %@",filePath);
        RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
        config.fileURL = [NSURL URLWithString:filePath];
        config.objectClasses = @[MyClass.class, MyOtherClass.class];
        config.readOnly = NO;
        int currentVersion = 1.0;
        config.schemaVersion = currentVersion;
        config.migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
           // 这里是设置数据迁移的block
            if (oldSchemaVersion < currentVersion) {
            }
        };
        [RLMRealmConfiguration setDefaultConfiguration:config];
    }
    

    拓展:

    // 查询指定的 Realm 数据库
    RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 获得一个指定的 Realm 数据库
    RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 从该 Realm 数据库中,检索所有狗狗
    

    创建数据库主要设置RLMRealmConfiguration,设置数据库名字和存储地方。把路径以及数据库名字拼接好字符串,赋值给fileURL即可。

    objectClasses这个属性是用来控制对哪个类能够存储在指定 Realm 数据库中做出限制。例如,如果有两个团队分别负责开发您应用中的不同部分,并且同时在应用内部使用了 Realm 数据库,那么您肯定不希望为它们协调进行数据迁移您可以通过设置RLMRealmConfiguration的 objectClasses属性来对类做出限制。objectClasses一般可以不用设置。
    readOnly是控制是否只读属性。

    2.建表

    • 创建简单数据模型
    #import <Realm/Realm.h>
    
    @interface MJCoutryModel : RLMObject
    
    @property (nonatomic, copy) NSString *countryId;
    
    @property (nonatomic, copy) NSString *country;
    
    @property (nonatomic, copy) NSString *dialCode;
    
    @end
    
    RLM_ARRAY_TYPE(MJCoutryModel)
    

    可以设置配置

    //主键
    + (NSString *)primaryKey {
        return @"countryId";
    }
    
    ////设置属性默认值
    //+ (NSDictionary *)defaultPropertyValues {
    //    return @{@"dialCode":@"00" };
    //}
    
    //设置忽略属性,即不存到realm数据库中
    + (NSArray<NSString *> *)ignoredProperties {
        return @[@"country"];
    }
    
    //一般来说,属性为nil的话realm会抛出异常,但是如果实现了这个方法的话,就只有countryId为nil会抛出异常,也就是说现在dialCode属性可以为空了
    + (NSArray *)requiredProperties {
        return @[@"countryId"];
    }
    
    //设置索引,可以加快检索的速度
    + (NSArray *)indexedProperties {
        return @[@"countryId"];
    }
    
    • 创建嵌套数据模型
    #import <Realm/Realm.h>
    @class Person;
    // 狗狗的数据模型
    @interface Dog : RLMObject
    @property NSString *name;
    @property Person   *owner;
    @end
    RLM_ARRAY_TYPE(Dog) // 定义RLMArray<Dog>
    // 狗狗主人的数据模型
    @interface Person : RLMObject
    @property NSString      *name;
    @property NSDate        *birthdate;
    // 通过RLMArray建立关系
    @property RLMArray<Dog> *dogs;
    @end
    RLM_ARRAY_TYPE(Person) // 定义RLMArray<Person>
    

    注意:RLMObject 官方建议不要加上 Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)假如设置了,这些attributes会一直生效直到RLMObject被写入realm数据库。

    3.增

        Dog *myDog = [[Dog alloc] init];
        myDog.name = @"小花";
        
        Dog *yourDog = [[Dog alloc] init];
        yourDog.name = @"小黑";
        
        Person *me = [[Person alloc] initWithValue:@[@"小明",[NSDate dateWithTimeIntervalSinceNow:1],@[myDog,yourDog]]];
        
        yourDog.owner = me;
        myDog.owner = me;
        
        
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [realm addObject:yourDog];
        [realm addObject:myDog];
        [realm addObject:me];
        [realm commitWriteTransaction];
    

    使用Realm进行数据管理的方式:
    方式一:

     RLMRealm *realm = [RLMRealm defaultRealm];
    
    // 开放RLMRealm事务
    [realm beginWriteTransaction];
    
    // 在开放开放/提交事务之间进行数据处理
    
    // 提交事务
    [realm commitWriteTransaction];
    

    方式二:

    [realm transactionWithBlock:^{
    
     // 进行数据处理
    
    }];
    

    4.删

    清空所有数据

       
        RLMRealm *realm = [RLMRealm defaultRealm];
        RLMResults<Person *> *personResults = [Person allObjects];
        if (personResults.count > 0) {
            [realm beginWriteTransaction];
            [realm deleteObjects:personResults];
            [realm commitWriteTransaction];
        }
        
        RLMResults<Dog *> *dogResults = [Dog allObjects];
            [realm beginWriteTransaction];
            [realm deleteAllObjects];
            [realm commitWriteTransaction];
    

    清空某条数据

       
        RLMRealm *realm = [RLMRealm defaultRealm];
        RLMResults<MJCoutryModel *> *walletResults = [MJCoutryModel allObjects];
        MJCoutryModel *wallet1 = [walletResults firstObject];
    
        [realm beginWriteTransaction];
        [realm deleteObject:wallet1];
        [realm commitWriteTransaction];
    
    

    5.查
    数据库查询

    // 查询默认的 Realm 数据库
    RLMResults *dogs = [Dog allObjects]; // 从默认的 Realm 数据库中,检索所有狗狗
    

    如果有需要,也可以查询指定的数据库

    // 查询指定的 Realm 数据库
    RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 获得一个指定的 Realm 数据库
    RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 从该 Realm 数据库中,检索所有狗狗
    

    条件查询
    1.使用断言字符串查询:

    RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黄色' AND name BEGINSWITH '大'" ascending:YES];
    

    2.使用 NSPredicate 查询

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
                                                     @"棕黄色", @"大"];
    RLMResults *tanDogs = [Dog objectsWithPredicate:pred];
    

    3.链式查询
    如果我们想获得获得棕黄色狗狗的查询结果,并且在这个查询结果的基础上再获得名字以“大”开头的棕黄色狗狗。

    RLMResults *tanDogs = [Dog objectsWhere:@"color = '棕黄色'"];
    RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH '大'"];
    

    6.改

    • 当没有主键的情况下,需要先查询,再修改数据。
       // 先查询
        RLMRealm *realm = [RLMRealm defaultRealm];
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"dialCode = %@ AND country BEGINSWITH %@",@"123", @"中国"];
        RLMResults<MJCoutryModel *> *walletResults = [MJCoutryModel objectsWithPredicate:pred];
       // 再修改
        for (int i = 0; i < walletResults.count; i++) {
            MJCoutryModel *wallet = walletResults[i];
            [realm beginWriteTransaction];
            wallet.country = @"托马斯品钦";
            [realm commitWriteTransaction];
        }
    
    • 当有主键的情况下,有以下几个非常好用的API
        RLMRealm *realm = [RLMRealm defaultRealm];
        
        MJCoutryModel *country = [[MJCoutryModel alloc] init];
        country.countryId = @"21";
        country.country = @"ee21";
        country.dialCode = @"22";
        
        [realm beginWriteTransaction];
        // 方法一
        [MJCoutryModel createOrUpdateInRealm:realm withValue:country];
        // 方法二
        [realm addOrUpdateObject:country];
        
        [realm commitWriteTransaction];
    

    addOrUpdateObject: 会去先查找有没有传入的Car相同的主键,如果有,就更新该条数据。这里需要注意,addOrUpdateObject这个方法不是增量更新,所有的值都必须有,如果有哪几个值是null,那么就会覆盖原来已经有的值,这样就会出现数据丢失的问题。

    createOrUpdateInRealm:withValue:这个方法是增量更新的,后面传一个字典,使用这个方法的前提是有主键。方法会先去主键里面找有没有字典里面传入的主键的记录,如果有,就只更新字典里面的子集。如果没有,就新建一条记录。

    数据迁移

    当您使用任意一个数据库时,您随时都可能打算修改您的数据模型。通过设置 RLMRealmConfiguration.schemaVersion 以及RLMRealmConfiguration.migrationBlock 可以定义一个迁移操作以及与之关联的架构版本。 迁移闭包将会提供提供相应的逻辑操作,以让数据模型从之前的架构转换到新的架构中来。 每当通过配置创建完一个 RLMRealm 之后,迁移闭包将会在迁移需要的时候,将给定的架构版本应用到更新 RLMRealm 操作中。
    如下所示是最简单的数据迁移的必需流程:

    // 在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置
    
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.schemaVersion = 2;
    config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion)
    {
        // enumerateObjects:block: 遍历了存储在 Realm 文件中的每一个“Person”对象
        [migration enumerateObjects:MJCoutryModel.className block:^(RLMObject *oldObject, RLMObject *newObject) {
            // 只有当 Realm 数据库的架构版本为 0 的时候,才添加 “fullName” 属性
            if (oldSchemaVersion < 1) {
                newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
            }
            // 只有当 Realm 数据库的架构版本为 0 或者 1 的时候,才添加“email”属性
            if (oldSchemaVersion < 2) {
                newObject[@"email"] = @"";
            }
           // 替换属性名
           if (oldSchemaVersion < 3) { // 重命名操作应该在调用 `enumerateObjects:` 之外完成 
                [migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"]; }
        }];
    };
    [RLMRealmConfiguration setDefaultConfiguration:config];
    // 现在我们已经成功更新了架构版本并且提供了迁移闭包,打开旧有的 Realm 数据库会自动执行此数据迁移,然后成功进行访问
    [RLMRealm defaultRealm];
    

    参考链接

    Realm官网
    Realm官方文档
    Realm GitHub
    参考博客一
    参考博客二

    相关文章

      网友评论

          本文标题:Realm的简单使用

          本文链接:https://www.haomeiwen.com/subject/lzvzittx.html