WCDB的基本使用

前言

WCDB是微信移动端团队开源的移动端数据库组件,提供了一个高效、完整、易用的移动端存储方案。第一次应用到WCDB还是在现公司的工程中,由于现在的team成员主要来自鹅厂,在工程中应用到前东家的东西也是理所当然,这同时也充分说明了WCDB的易用性,不好用谁会继续再使用它呢?本文主要是对WCDB做简单的介绍以及使用方法的归纳总结。

为什么选择WCDB

  • 之所以选择WCDB,主要还是因为它的高效、完整、易用性。
  1. 高效
    WCDB支持多线程的读读、读写并发以及写写串行执行,在批量写操作的性能测试中,WCDB性能是FMDB的180%左右。


    批量读写操作性能对比.jpg

    而在多线程读写操作中,WCDB的多线程读写操作性能优于FMDB 62% ,而多线程读操作基本与FMDB持平。FMDB在多线程写测试中,直接返回错误SQLITE_BUSY,因此无法比较。而基于SQLite的机制,WCDB的多线程写操作实质也是串行执行,但不会出错导致操作中断。


    多线程读写操作性能对比.jpg
  2. 完整
    加密:WCDB提供基于SQLCipher的数据库加密。
    损坏修复:WCDB内建了Repair Kit用于修复损坏的数据库。
    WCDB提供接口直接获取SQL的执行耗时,可用于监控性能。
    反注入:WCDB内建了对SQL注入的保护
  3. 易用
    WCDB的查询语言是用WINQ进行查询,无需为了拼接SQL字符串写很长的代码。
    WCDB的对象关系映射也非常方便,可以很便捷地定义表、索引、约束和增删改查操作等。

WCDB的使用

  • 创建数据库
-(WCTDatabase *)db {
    if (_db) {
        return _db;
    }
    //获取沙盒根目录
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    // 文件路径
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"RSDBService.sqlite"];
    NSLog(@"path = %@",filePath);
    _db = [[WCTDatabase alloc]initWithPath:filePath];
    if (![_db canOpen]) {
        NSLog(@"RSDBService.sqlite canOpen fail");
        [_db createTableAndIndexesOfName:@"" withClass:[NSArray class]];
    }
    return _db;
}
  • 对象关系映射

WCDB使用内建的宏实现ORM的功能,通过ORM可以达到直接通过Object进行数据库操作。此处需要注意的一点是,由于WCDB是基于Objective C++,如果在model的头文件中引入了<WCDB/WCDB.h>,就需要把.m文件改变为.mm文件。为了不影响到使用model的controller或者view类,此处可以用category特性将wcdb的引用隔离。在category中引用<WCDB/WCDB.h>,并遵守WCTTableCoding协议,使用WCDB_PROPERTY将声明绑定到数据库表的字段。以下用一个好友关系的contactModel做举例说明

首先是category文件,category中需要引入<WCDB/WCDB.h>并遵守WCTTableCoding协议

#import "RSContactModel.h"
#import <WCDB/WCDB.h>

@interface RSContactModel (WCTTableCoding) <WCTTableCoding>

WCDB_PROPERTY(uid)
WCDB_PROPERTY(nickName)
WCDB_PROPERTY(avatarUrl)
WCDB_PROPERTY(sex)
WCDB_PROPERTY(delFlag)
WCDB_PROPERTY(addFriendImgUrl)
WCDB_PROPERTY(registerTime)
WCDB_PROPERTY(addFriendTime)

@end

然后是.h文件,在.h中主要做的就是将model所需要暴露的属性暴露出来,以供其他类使用

#import <UIKit/UIKit.h>
#import "RSModel.h"
#import "Spcgicommdef.pbobjc.h"

@interface RSContactModel : RSModel
@property (nonatomic, strong) NSString *nickName;
@property (nonatomic, assign) long long uid;
@property (nonatomic, strong) NSString *avatarUrl;
@property (nonatomic, assign) RSenSex sex;
@property (nonatomic, assign) RSenDelFlag delFlag;
@property (nonatomic, strong) NSString *addFriendImgUrl;
@property (nonatomic, assign) int32_t registerTime;
@property (nonatomic, assign) int32_t addFriendTime;
@end

最后是.m文件,在.m文件中需要定义类文件中绑定到数据库表的字段以及主键的设置、索引的设置以及约束等。并且在init方法中通过dispacth_once初始化数据库表。

#import "RSContactModel+WCTTableCoding.h"
#import "RSContactModel.h"
#import <WCDB/WCDB.h>
#import "RSDBService.h"

@implementation RSContactModel

WCDB_IMPLEMENTATION(RSContactModel)
WCDB_SYNTHESIZE(RSContactModel, nickName)
WCDB_SYNTHESIZE(RSContactModel, uid)
WCDB_SYNTHESIZE(RSContactModel, avatarUrl)
WCDB_SYNTHESIZE(RSContactModel, sex)
WCDB_SYNTHESIZE(RSContactModel, addFriendImgUrl)
WCDB_SYNTHESIZE_DEFAULT(RSContactModel, delFlag, 0);
WCDB_SYNTHESIZE(RSContactModel, registerTime)
WCDB_SYNTHESIZE(RSContactModel, addFriendTime)
WCDB_UNIQUE(RSContactModel, uid)
WCDB_NOT_NULL(RSContactModel, uid)
-(instancetype)init {
    self = [super init];
    if (self) {
        static dispatch_once_t token;
        dispatch_once(&token, ^{
            [RSContactModel createDBTable];
        });
    }
    return self;
}

+(void)createDBTable {
    if ([[RSDBService db] createTableAndIndexesOfName:NSStringFromClass([RSContactModel class]) withClass:[RSContactModel class]]) {
        NSLog(@"creat table RSContactModel success");
    } else {
        NSLog(@"creat table RSContactModel fail");
    }
}
@end

WCDB_PROPERTY用于在头文件中声明绑定到数据库表的字段。

WCDB_IMPLEMENTATION,用于在类文件中定义绑定到数据库表的类。同时,该宏内实现了WCTTableCoding。因此,开发者无须添加更多的代码来完成WCTTableCoding的接口

WCDB_SYNTHESIZE,用于在类文件中定义绑定到数据库表的字段。

WCDB_PRIMARY用于定义主键

WCDB_PRIMARY_AUTO_INCREMENT 用于定义自增主键

WCDB_INDEX用于定义索引

WCDB_UNIQUE用于定义唯一约束

WCDB_NOT_NULL用于定义非空约束

  • 增删改查CRUD

对数据库访问的接口实现建议提供专门的Service类来进行操作,比如对好友关系的数据库模型可以提供一个专门的RSContactService来对RSContact表进行操作。如果熟悉RAC的话,可以在Service中结合RAC的信号来通知业务层说该数据库表有更新。

1.增:

-(BOOL)saveContactList:(NSArray *)contactList {
    //contactList中为服务端下发的contact列表
    NSMutableArray *tmp = [[NSMutableArray alloc] init];
    for (RSContact *contact in contactList) {
        RSContactModel *model = [[RSContactModel alloc] init];
        model.uid = contact.uin;
        model.nickName = contact.nickName;
        model.avatarUrl = contact.headImgURL;
        model.sex = (RSenSex)contact.sex;
        model.delFlag = (RSenDelFlag)contact.delFlag;
        model.addFriendImgUrl = contact.addFriendImgURL;
        model.registerTime = contact.registerTime;
        model.addFriendTime = contact.addFriendTime;
        [tmp addObject:model];
    }
    BOOL result = [[RSDBService db] insertOrReplaceObjects:tmp into:NSStringFromClass([RSContactModel class])];
    if (result) {
        [self.updateSignal sendNext:@(YES)];
        //如果result为true表示插入数据库成功,发送一个数据库更新的信号
    }
    return result;
}

2.删:

删除RSContactModel表中uid字段值为testUid的记录

    BOOL result = [[RSDBService db] deleteObjectsFromTable:NSStringFromClass([RSContactModel class]) where: RSContactModel.uid.is(testUid)];

3.改:

以下事例为删除某个uid为uid值的好友关系的时候,将contactModel中的delFlag更新为已删除的代码。实际上就是更新该记录中的delFlag的字段

- (BOOL)deleteContactWithUid:(long long)uid {
    RSContactModel *contactModel = [[RSContactModel alloc] init];
    contactModel.delFlag = RSenDelFlag_DelflagNotExist;
    BOOL result = [[RSDBService db] updateRowsInTable:NSStringFromClass([RSContactModel class]) onProperty:RSContactModel.delFlag withObject:contactModel where:RSContactModel.uid.is(uid)];
    if (result) {
        [self.updateSignal sendNext:@(YES)];
        //如果result为true表示修改数据库成功,发送一个数据库更新的信号
    }
    return result;
}

4.查:

数据库查询的接口就更多了,这里举例为根据uid的array查询表中uid为array中的值的记录。

-(NSArray<RSContactModel *>*)getContactsByUids:(NSArray *)uids {
    NSArray *tmp = [[RSDBService db] getObjectsOfClass:[RSContactModel class] fromTable:NSStringFromClass([RSContactModel class]) where:RSContactModel.uid.in(uids)];
    return tmp;
}

  • 事务transaction

WCDB的事务有两种写法,一种是通过block来实现,另一种是通过获取WCTTransaction来实现,block的方式使用更简单,但是WCTTransaction的方式更易于传递。

//Block的方式
BOOL commited = [[RSDBService db] runTransaction:^BOOL{
    [[RSDBService db] insertObject:contact into:NSStringFromClass([RSContactModel class])];
    return YES;
}];

//WCTTransaction方式
WCTTransaction *transaction = [[RSDBService db] getTransaction];
BOOL result = [transaction begin];
[[RSDBService db] insertObject:contact into:NSStringFromClass([RSContactModel class])];
result = [transaction commit];
if(!result) {
    NSLog(@"%@",[transaction getError]);
}

扩展阅读

微信移动端数据库组件WCDB系列(一)-iOS基础篇

微信移动端数据库组件WCDB系列(二) — 数据库修复三板斧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容