這里只是提到了一些基礎(chǔ)是用法。深度用法阳惹,可以前往官網(wǎng)學(xué)習(xí)谍失。 關(guān)于WCDB的背景和優(yōu)缺點(diǎn),這里也不做介紹莹汤,大家可以自行百度快鱼。
類字段綁定(ORM)
在WCDB內(nèi),ORM(Object Relational Mapping)是指
將一個(gè)ObjC的類纲岭,映射到數(shù)據(jù)庫的表和索引止潮;
將類的property,映射到數(shù)據(jù)庫表的字段唆樊;
這一過程。通過ORM仓洼,可以達(dá)到直接通過Object進(jìn)行數(shù)據(jù)庫操作,省去拼裝過程的目的舌缤。
WCDB通過內(nèi)建的宏來實(shí)現(xiàn)ORM的功能。
首先創(chuàng)建一個(gè)model:
.h
#import "DetailModel.h"
@interface Draft1 : NSObject
@property int localID;
@property(retain) NSString *content;
@property(retain) NSString *title;
@property(retain) NSDate *createTime;
@property(retain) NSDate *modifiedTime;
@property(assign) int unused;
@property (nonatomic, strong) DetailModel *model;
@end
.mm
#import <WCDB/WCDB.h>
@implementation Draft1
WCDB_IMPLEMENTATION(Draft1)
WCDB_SYNTHESIZE(Draft1, localID)
WCDB_SYNTHESIZE(Draft1, content)
WCDB_SYNTHESIZE(Draft1, createTime)
WCDB_SYNTHESIZE(Draft1, modifiedTime)
WCDB_SYNTHESIZE(Draft1, model)
WCDB_SYNTHESIZE(Draft1, title)
//主鍵
WCDB_PRIMARY(Draft1, localID)
WCDB_INDEX(Draft1, "_index", createTime)
@end
由于WCDB是結(jié)合c++寫的,引用#import <WCDB/WCDB.h>的文件.m里面都要改成.mm后綴的,所以一般上為了隔離model,不讓view喝viewController里面也改成.mm后綴的,我們寫一個(gè)model的分類,遵守WCTTableCoding協(xié)議并寫WCDB_PROPERTY(),WCDB編譯后項(xiàng)目里有快捷創(chuàng)建model類,直接創(chuàng)建出分類.
創(chuàng)建對應(yīng)model的分類
.h
#import <WCDB/WCDB.h>
NS_ASSUME_NONNULL_BEGIN
@interface Draft1 (wcdb)<WCTTableCoding>
WCDB_PROPERTY(localID)
WCDB_PROPERTY(content)
WCDB_PROPERTY(title)
WCDB_PROPERTY(createTime)
WCDB_PROPERTY(modifiedTime)
WCDB_PROPERTY(model)
@end
將一個(gè)已有的ObjC類進(jìn)行ORM綁定的過程如下:
定義該類遵循WCTTableCoding協(xié)議囚似。可以在類聲明上定義募狂,也可以通過文件模版在category內(nèi)定義谓罗。
使用WCDB_PROPERTY宏在頭文件聲明需要綁定到數(shù)據(jù)庫表的字段揭措。
使用WCDB_IMPLEMENTATIO宏在類文件定義綁定到數(shù)據(jù)庫表的類。
使用WCDB_SYNTHESIZE宏在類文件定義需要綁定到數(shù)據(jù)庫表的字段躬充。
簡單幾行代碼以政,就完成了將類和需要的字段綁定到數(shù)據(jù)庫表的過程。這三個(gè)宏在名稱和使用習(xí)慣上抖誉,也都和定義一個(gè)ObjC類相似樊零,以此便于記憶。
除此之外塑悼,WCDB還提供了許多可選的宏烹植,用于定義數(shù)據(jù)庫索引草雕、約束等嘱巾,如:
WCDB_PRIMARY用于定義主鍵
WCDB_INDEX用于定義索引
WCDB_UNIQUE用于定義唯一約束
WCDB_NOT_NULL用于定義非空約束
具體用法
在我們的項(xiàng)目中,定義好的接口(SEEDDBManagerProtocol) 可以滿足一般場景的增刪改查绪杏。
#import <WCDB/WCDB.h>
@protocol SEEDDBManagerProtocol<NSObject>
@required
//便利構(gòu)造器
+ (instancetype)shareManager;
#pragma mark -- create table
/// 創(chuàng)建具體的表
/// @param tableName 表名
/// @param modelClass 對應(yīng)的model的類
- (BOOL)createTableWithName:(NSString *)tableName
modelClass:(Class)modelClass;
@optional
#pragma mark -- insert
/// 在某個(gè)已知的表里或油,插入新單個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
- (BOOL)insertData:(WCTObject *)message;
/// 在某個(gè)已知的表里寞忿,插入(已經(jīng)存在該數(shù)據(jù),就更新)新單個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
- (BOOL)insertOrReplaceObject:(WCTObject *)message;
/// 在某個(gè)表里顶岸,插入單個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
/// @param tableName 表名
/// @param modelClass 對應(yīng)model的類名
- (BOOL)insertData:(WCTObject *)message
withTableName:(NSString *)tableName
modelClass:(Class)modelClass;
/// 在某個(gè)表里腔彰,插入(已經(jīng)存在該數(shù)據(jù),就更新)單個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
/// @param tableName 表名
/// @param modelClass 對應(yīng)model的類名
- (BOOL)insertOrReplaceObject:(WCTObject *)message
withTableName:(NSString *)tableName
modelClass:(Class)modelClass;
#pragma mark -- update
/// 在某個(gè)已知的表里辖佣,更新草稿數(shù)據(jù)
/// @param message 需要更新的數(shù)據(jù)
- (BOOL)updateData:(WCTObject *)message;
/// 在某個(gè)表里霹抛,更新單個(gè)數(shù)據(jù)
/// @param tableName 表名
/// @param property 需要更新的屬性
/// @param value 對應(yīng)的值
- (BOOL)updateAllRowsInTable:(NSString *)tableName
onProperty:(const WCTProperty &)property
withValue:(WCTValue *)value;;
#pragma mark -- select
/// 在某個(gè)已知的表里,根據(jù)條件獲取某個(gè)單獨(dú)的草稿數(shù)據(jù)
/// @param where 條件
- (WCTObject *)selectData:(const WCTCondition &)where;
/// 在某個(gè)表里卷谈,根據(jù)條件獲取某個(gè)單獨(dú)的草稿數(shù)據(jù)
/// @param where 條件
/// @param tableName 表名
/// @param modelClass 對應(yīng)model的類
- (WCTObject *)selectData:(const WCTCondition &)where
withTableName:(NSString *)tableName
modelClass:(Class)modelClass;
#pragma mark -- delete
/// 在某個(gè)已知的表里杯拐,刪除某個(gè)數(shù)據(jù)
/// @param where 條件
- (BOOL)delegateData:(const WCTCondition &)where;
/// 在某個(gè)已知的表里,刪除某個(gè)數(shù)據(jù)
/// @param where 條件
/// @param tableName 表名
/// @param modelClass 對應(yīng)model的類
- (BOOL)delegateDataWithWhere:(const WCTCondition &)where
withTableName:(NSString *)tableName
modelClass:(Class)modelClass;
/**
未完待續(xù)......
*/
@end
創(chuàng)建數(shù)據(jù)庫
#define kDataBaseFileName @"SEEDDB.sqlite"
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static SEEDDBManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_instance == nil) {
_instance = [super allocWithZone:zone];
[_instance creatDatabase];
}
});
return _instance;
}
+ (instancetype)shareManager {
return [[self alloc] init];
}
//創(chuàng)建數(shù)據(jù)庫
- (BOOL)creatDatabase{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dbPath = [documentPath stringByAppendingString:kDataBaseFileName];
self.database = [[WCTDatabase alloc] initWithPath:dbPath];
self.database.tag = 0;
if ([self.database canOpen]) {
NSLog(@"創(chuàng)建數(shù)據(jù)庫成功");
}else{
NSLog(@"創(chuàng)建數(shù)據(jù)庫失敗");
return NO;
}
return YES;
}
創(chuàng)建具體的表
//創(chuàng)建具體的表
- (BOOL) createTableWithName:(NSString *)tableName modelClass:(Class)modelClass{
//創(chuàng)建表 注:該接口使用的是IF NOT EXISTS的SQL世蔗,因此可以用重復(fù)調(diào)用端逼。不需要在每次調(diào)用前判斷表或索引是否已經(jīng)存在。
BOOL result = [self.database createTableAndIndexesOfName:tableName withClass:modelClass];
if (!result) {
NSLog(@"創(chuàng)建表失敗");
return NO;
}
NSLog(@"創(chuàng)建表成功");
return YES;
}
創(chuàng)建SEEDDBManager 的分類污淋,來跟具體業(yè)務(wù)綁定顶滩。
#define kTable_Name @"TableName"
@implementation SEEDDBManager (businessOne)
/// 在某個(gè)已知的表里,插入(已經(jīng)存在該數(shù)據(jù)寸爆,就更新)新單個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
- (BOOL)insertOrReplaceObject:(Draft1 *)message{
BOOL result = [self.database insertOrReplaceObject:message into:kTable_Name];
//關(guān)閉數(shù)據(jù)庫,_database如果能自己釋放的話,會(huì)自動(dòng)關(guān)閉,就不用手動(dòng)調(diào)用關(guān)閉了
[self.database close];
if (!result) {
NSLog(@"插入失敗");
return NO;
}else{
NSLog(@"插入成功");
return YES;
}
}
/// 在某個(gè)已知的表里礁鲁,根據(jù)條件獲取某個(gè)單獨(dú)的草稿數(shù)據(jù)
/// @param where 條件
- (WCTObject *)selectData:(const WCTCondition &)where{
WCTTable *table = [self.database getTableOfName:kTable_Name withClass:Draft1.class];
WCTObject *objc = [ table getOneObjectWhere:where];
return objc;
}
/// 在某個(gè)已知的表里,刪除某個(gè)數(shù)據(jù)
/// @param message 需要插入的數(shù)據(jù)
- (BOOL)delegateData:(Draft1 *)message{
//刪除
//DELETE FROM message WHERE localID>0;
BOOL result = [self.database deleteObjectsFromTable:kTable_Name
where:Draft1.localID == message.localID];
return result;
}
這樣可以使用全局統(tǒng)一的SEEDDBManager單利對象來調(diào)用數(shù)據(jù)庫方法赁豆。 具體的業(yè)務(wù)代碼則由各自SEEDDBManager的分類管理仅醇。
關(guān)于model的嵌套
在業(yè)務(wù)場景中,很容易遇到在Draft1模型的內(nèi)部包含有一個(gè)其他的model魔种。
不做任何處理的話析二,是沒法正常使用的。
WCDB提供了一些具體的類型和方法如下:
需要在內(nèi)部嵌套的model類节预,做一些處理甲抖。 需要實(shí)現(xiàn)
- (id)columnTypeForWCDB:
- (instancetype)unarchiveWithWCTValue:(WCTValue *)value;
- (id)archivedWCTValue;
這三個(gè)方法。
創(chuàng)建DetailModel
.h
#import <WCDB/WCDB.h>
@interface DetailModel : NSObject<WCTColumnCoding,WCTTableCoding,NSCoding>
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *content;
WCDB_PROPERTY(title)
WCDB_PROPERTY(content)
@end
. mm
@implementation DetailModel
WCDB_IMPLEMENTATION(DetailModel)
WCDB_SYNTHESIZE(DetailModel, title)
WCDB_SYNTHESIZE(DetailModel, content)
- (id)archivedWCTValue {
return [NSKeyedArchiver archivedDataWithRootObject:self];;
}
+ (WCTColumnType)columnTypeForWCDB {
return WCTColumnTypeBinary;
}
+ (instancetype)unarchiveWithWCTValue:(WCTValue *)value {
if (value) {
@try {
DetailModel *model = [NSKeyedUnarchiver unarchiveObjectWithData:value];
return model;
}
@catch (NSException *exception) {
NSLog(@"exception:%@",[exception description]);
}
}
return nil;
}
//歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (unsigned int i =0; i<outCount; i++) {
Ivar ivar = ivars[i];
NSString*key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
}
//解歸檔
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
unsigned int OutCount = 0;
Ivar *ivars = class_copyIvarList([self class], &OutCount);
for (unsigned int i =0; i<OutCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}
@end
這樣就可以model嵌套了心铃。