FMDB 使用方法【转】

(一)Realm简单介绍
1.什么是Realm
Realm 是由美国YCombinator孵化的创业团队历时几年打造的第一款针对移动设备设计的数据库。是一个跨平台的移动数据库引擎,目前支持iOS、Android平台,同时支持Objective-C、Swift、Java、React Native、Xamarin等多种编程语言。Realm并不是对SQLite或者CoreData的简单封装, 是由核心数据引擎C++打造,是拥有独立的数据库存储引擎,可以方便、高效的完成数据库的各种操作
2.Realm的特性
a.移动设备支持:Realm是第一个针对手机平板和可穿戴设备涉及的数据库,基本全平台支持。
b.简单:Realm的安装和借入简易,不同于Core Data,不需要开发者配置模型层结构,从开始接触到上手使用,只需要短短几分钟而已。
c.现代:关系型数据库,支持泛型。
d.快速:官方称比SQLite常规操作要快。
e.跨平台:同时支持多个平台,节省资源。
3.Realm的资源
官方文档:https://realm.io (学习的首选参考手册)
GitHub:https://github.com/realm/realm-cocoa
可视化工具: Realm Browser

image

如果需要调试, 可以通过NSHomeDirectory()打印出Realm数据库地址, 找到对应的Realm文件, 然后用Realm Browser可视化工具打开即可
Xcode 插件有助于创建Realm数据模型

image

(二)Realm的安装
(1)手动安装
a.* 要使用 Realm 数据库, 首先要导入 Realm.framework 这个框架, 从Realm官网上可以下载,我使用的是realm-objc-3.14.1 这个版本, 下载解压完成后, 将iOS文件夹中的 static文件中的静态库拖到工程中

image

b.然后点击 Targets, 选择工程, 点击 Build Phases, 添加libz.tbd 和 libc++.tbd 两个库

image

c.点击 plugin 文件中的项目, 运行,然后退出 XCode, 重启 XCode, 就可以安装成功插件, 我们就可以利用插件自动生成模型类

image

d.安装成功插件后, command + N 在新建文件的时候滑到最底端就可以看见Realm Model Object, 它就是Realm 数据库存储的模型对象, 你要存储的数据都是继承自这个类的. 所以我们新建 Realm Model Object 文件时, 就会自动帮我们建立一个.h 和.m 文件,就跟创建继承NSObject类是一样的

image

e.在用到的文件中引入#import <Realm/Realm.h> 即可使用
⚠️:官方文档介绍了四种安装说明,我只整理了一种最简单快速的方法
(2)工程中使用
(三)Realm定义的类
Realm框架中,定义了二十个核心类、常量、枚举类型、协议等,常用的如:RLMRealm类、RLMObject类、RLMResults类等, 我们可以从Realm官方网站上查看所有的定义以及使用说明
1.Realm类
一个Realm类的对象可以认为是一个Realm数据库,Realm数据库既可以存储到硬盘上,也可以存储到内存中。Realm类是框架的核心类,如同FMDB中的FMDatabase ,Core Data管理对象的上下文managed object context一样

//获取默认的Realm数据库
+ (instancetype)defaultRealm;
//实例化一个realm数据库,根据配置参数获取对应的Realm数据库
+ (nullable instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error;
//根据指定的文件URL初始化一个Realm数据库
+ (instancetype)realmWithURL:(NSURL *)fileURL;
//对Realm 数据库进行读写操作之前,只能开启一个写事务
- (void)beginWriteTransaction;
//对Realm数据库进行读写操作完成之后,关闭对应的写事务
- (void)commitWriteTransaction NS_SWIFT_UNAVAILABLE("");
//没有足够的磁盘空间来保存写入或由于意外的I / O错误,此方法可能会失败, 并返回error信息
- (BOOL)commitWriteTransaction:(NSError **)error;
//在当前写入事务中提交所有写入操作,而不收到此写入事件的特定通知
- (BOOL)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(NSError **)error;
//回滚在当前写入事务期间进行的所有写入并结束事务
- (void)cancelWriteTransaction;
//执行写入事务块内特定的操作
- (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block NS_SWIFT_UNAVAILABLE("");
- (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)error;
//添加或更新一个对象
- (void)addObject:(RLMObject *)object;
//将现有对象添加或更新到Realm中, 有则更新没有则插入
- (void)addOrUpdateObject:(RLMObject *)object;
//添加或更新多个对象
- (void)addObjects:(id<NSFastEnumeration>)objects;
- (void)addOrUpdateObjects:(id<NSFastEnumeration>)objects;
//删除对象
- (void)deleteObject:(RLMObject *)object;
- (void)deleteObjects:(id)array;
- (void)deleteAllObjects;

  1. RLMObject类
    在Realm数据库中存储的都是RMObject对象,RLMObject类是所有可以存储在Realm数据库中的对象的根类。
    在RLMObject类中,我们可以添加属性,添加的属性类型可以支持如下类型:
    NSString:字符串
    NSInteger, int, long, float, double:数字型,注意没有CGFloat
    BOOL/bool:布尔型
    NSDate:日期型
    NSData:二进制字符型
    NSNumber<X>: 其中X必须RLMInt, RLMFloat, RLMDouble或 RLMBool类型
    RLMArray<X>: 其中X必须是RLMObject类的子类, 用于建模多对多关系
    RLMObject的子类,用于建模多对一关系
//创建RLMObject对象, 传入一个NSArray或NSDictionary实例来设置对象属性的值
- (instancetype)initWithValue:(id)value NS_DESIGNATED_INITIALIZER;
//获取RLMObject对象的类名
+ (NSString *)className;
//在Realm数据库中,获取该RLMObject类的所有对象
+ (RLMResults *)allObjects;

//根据查询条件返回满足条件的所有RLMObject类的对象
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...;

//使用默认Realm中的给定主键检索此对象类型的单个实例
+ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey;

//从指定的Realm返回此对象类型的所有对象
+ (nonnull RLMResults *)allObjectsInRealm:(nonnull RLMRealm *)realm;

//返回与指定Realm中给定谓词匹配的此对象类型的所有对象
+ (nonnull RLMResults *)objectsInRealm:(nonnull RLMRealm *)realm where:(nonnull NSString *)predicateFormat, ...;

  1. RLMResults类
    当我们执行一个查询操作后,查询出满足条件的RLMObject对象会存放在一个RLMResults对象中,RLMResults类是一个数组类型的数据结构,因此在其类定义中,提供了很多与数组类似的属性和方法。
//结果集合中的对象个数
@property (readonly, assign, nonatomic) NSUInteger count;
//结果集合中对象的类型
@property (readonly, assign, nonatomic) RLMPropertyType type;
//管理此结果集合的Realm对象
@property (readonly, nonatomic) RLMRealm *_Nonnull realm;
//结果集合中包含的对象的类名称
@property (readonly, copy, nonatomic, nullable) NSString *objectClassName;
//返回结果集合中的第一个对象
- (nullable RLMObjectType)firstObject;
//返回结果集合中的最后一个对象
- (nullable RLMObjectType)lastObject;
//根据索引index获取其中的某个对象
- (RLMObjectType)objectAtIndex:(NSUInteger)index;
//根据对象返回其索引
- (NSUInteger)indexOfObject:(RLMObjectArgument)object;
//返回与谓词匹配的结果集合中第一个对象的索引
- (NSUInteger)indexOfObjectWhere:(nonnull NSString *)predicateFormat, ...;
//返回与结果集合中给定谓词匹配的所有对象
- (RLMResults<RLMObjectType> *)objectsWhere:(NSString *)predicateFormat, ...;
//返回RLMResults从现有结果集合中排序的内容
- (RLMResults<RLMObjectType> *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending;
//返回RLMResults与现有结果集合不同的内容
- (nonnull RLMResults<RLMObjectType> *)distinctResultsUsingKeyPaths:(nonnull NSArray<NSString *> *)keyPaths;

相关类的官方说明:https://realm.io/docs/objc/latest/api/Classes.html
(四)Realm的使用
1.存储对象
对于RLMObject类型的对象,我们可以直接对创建的对象进行存储,第一步, 初始化对象

    //1.属性赋值
    Dog *dog1 = [[Dog alloc]init];
    dog1.name = @"perter1";
    dog1.age = 1;
    //将数组转化为对象
    Dog *dog2 = [[Dog alloc]initWithValue:@[@"perter2",@2]];
    //将字典转化成对象
    Dog *dog3 = [[Dog alloc]initWithValue:@{@"name":@"perter3",@"age":@3}];

第二步就是把RLMObject对象写入Realm数据库, 同样有三种方式

//默认数据库
    RLMRealm *realm = [RLMRealm defaultRealm];
    //增
    //第一种增加方式
    [realm beginWriteTransaction];
    [realm addObject:dog1];
    [realm commitWriteTransaction];
    //第二种增加方式
    [realm transactionWithBlock:^{
        [realm addObject:dog2];
        [realm addObject:dog3];
    }];
    //第三种增加方式
    [realm transactionWithBlock:^{
        [Dog createInRealm:realm withValue:@{@"name":@"perter4",@"age":@4}];
    }];

Note:如果有多个写入操作,建议将每个写入操作放进一个单独的子线程中
2.查询操作
Realm的查询条件可以使用==、<=、<、>=、>、!=、BETWEEN、CONTAINS 以及 ENDSWITH等多种操作符

//查
    //Realm的查询条件可以使用==、<=、<、>=、>、!=、BETWEEN、CONTAINS 以及 ENDSWITH等多种操作符 关系型运算符
    //第一种:全量查询
    RLMResults *result = [Dog allObjects];
    NSLog(@"查询结果 %@",result);
    Dog *dog5 = [[Dog alloc]initWithValue:@{@"name":@"perter5",@"age":@5}];
    [realm transactionWithBlock:^{
        [realm addObject:dog5];
    }];
    NSLog(@"查询结果 %@",result);//实时查询更新
    //第二种:条件查询
    result = [Dog objectsWhere:@"age < 2"];
    NSLog(@"查询结果 %@",result);
    //第三种:数组排序
    RLMResults *result1 = [result sortedResultsUsingKeyPath:@"age" ascending:YES];
    NSLog(@"查询结果 %@",result1);//对原有数组进行排序之后得到一个新的数组
    //第四种:链式查询
    RLMResults *result2 = [result1 objectsWhere:@"age<3"];
    NSLog(@"查询结果 %@",result2);

3.更新操作
需要修改的模型一定是被Realm所管理的模型, 而且已经和磁盘上的对象进行地址映射

//更新
    //第一种更改在原有基础上进行更改
    [realm transactionWithBlock:^{
        dog1.name = @"newName1";
        dog2.name = @"newName2";

    }];
    RLMResults *result3 = [Dog objectsWhere:@"age < 2"];
    Dog *currentDog = result3.firstObject;
    [realm transactionWithBlock:^{
        currentDog.name = @"titanking";
    }];
    //第二种是有主键的情况下使用 addOrUpdateObject 或则createInRealm 方法进行更新 不然会crash: reason: ''Dog' does not have a primary key and can not be updated'
//    Dog *dog11 = [[Dog alloc]initWithValue:@{@"name":@"dog11",@"age":@11}];
//    [realm transactionWithBlock:^{
//        [realm addOrUpdateObject:dog11];
//
//    }];
//    [realm transactionWithBlock:^{
//        [Dog createInRealm:realm withValue:@{@"name":@"dog12",@"age":@12}];
//    }];
//    RLMResults *result4 = [Dog allObjects];
//    NSLog(@"查询结果 %@",result4);

Note:当有主键的情况下, 使用Update方法
addOrUpdateObject会去先查找有没有传入的Student相同的主键,如果有,就更新该条数据
这里需要注意,addOrUpdateObject这个方法不是增量更新,所有的值都必须有,如果有哪几个值是null,那么就会覆盖原来已经有的值,这样就会出现数据丢失的问题
createOrUpdateInRealm:withValue这个方法是增量更新的,后面传一个字典,使用这个方法的前提是有主键
方法会先去主键里面找有没有字典里面传入的主键的记录,如果有,就只更新字典里面的子集;如果没有,就新建一条记录
用addOrUpdateObject 或则createInRealm 方法进行更新前提是必须有主键, 不然会crash: reason: ''Dog' does not have a primary key and can not be updated'
4.删除操作

//删除
    //第一种 根据条件查询
    RLMResults *result5 = [Dog objectsWhere:@"age = 1"];
    Dog *deleteDog = [result5 firstObject];
    [realm transactionWithBlock:^{
        [realm deleteObject:deleteDog];
    }];
    RLMResults *result6 = [Dog objectsWhere:@"age < 6"];
    for (Dog *dog in result6) {
        [realm transactionWithBlock:^{
            [realm deleteObject:dog];
        }];
    }
    //如果有主键根据主键进行查询 没有就会crash
//    Dog *keyDog = [Dog objectInRealm:realm forPrimaryKey:@1];
//    [realm transactionWithBlock:^{
//        [realm deleteObject:keyDog];
//    }];
    ```
Note:删除方法中涉及到的primaryKey字段的前提是必须有主键
5.Realm数据库机制
上面用到的获取realm对象的方式都是通过defaultRealm来获取默认配置的realm对象,而我们想要创建不同的Realm表则通过RLMRealmConfiguration配置进行操作

//配置Realm数据库

[self setDefaultRealmForUser:@"zhangsan"];
RLMRealm *newRealm = [RLMRealm defaultRealm];

-(void)setDefaultRealmForUser:(NSString *)username {
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.fileURL = [[[config.fileURL URLByDeletingPathExtension] URLByAppendingPathComponent:username] URLByAppendingPathExtension:@"realm"];
    [RLMRealmConfiguration setDefaultConfiguration:config];
}

6.查看本地Realm数据库
找到本地Realm的路径,然后直接用Realm Browser 打开就可

image

以上几种情况是比较常见的,后期还会有更新补充,如有错误不吝赐教
感谢大神文档:https://www.jianshu.com/p/f415d07bc446
FMDB 使用方法:https://www.jianshu.com/p/7958d31c2a97

作者:123456789q
链接:https://www.jianshu.com/p/73b1f509a41f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

推荐阅读更多精彩内容