基本特性
易用,WCDB支持一句代碼即可將數(shù)據(jù)取出并組合為object。
WINQ(WCDB語言集成查詢):通過WINQ,開發(fā)者無須為了拼接SQL的字符串而寫一大坨膠水代碼。
-
ORM(Object Relational Mapping):在WCDB內(nèi),ORM(Object Relational Mapping)是指
將一個(gè)ObjC的類,映射到數(shù)據(jù)庫的表和索引;
將類的property,映射到數(shù)據(jù)庫表的字段; 高效,WCDB通過框架層和sqlcipher源碼優(yōu)化,使其更高效的表現(xiàn)。
多線程高并發(fā):WCDB支持多線程讀與讀、讀與寫并發(fā)執(zhí)行,寫與寫串行執(zhí)行。
完整,WCDB覆蓋了數(shù)據(jù)庫相關(guān)各種場景的所需功能。
加密:WCDB提供基于SQLCipher的數(shù)據(jù)庫加密。
損壞修復(fù):WCDB內(nèi)建了Repair Kit用于修復(fù)損壞的數(shù)據(jù)庫。
反注入:WCDB內(nèi)建了對SQL注入的保護(hù)。
WCDB需要對其建模,一個(gè)model可以遵守WCTTableCoding協(xié)議并用一些宏,用于定義數(shù)據(jù)庫索引、約束等.
//Message.h
@interface Message : NSObject <WCTTableCoding>
@property int localID;
@property(retain) NSString *content;
@property(retain) NSDate *createTime;
@property(retain) NSDate *modifiedTime;
WCDB_PROPERTY(localID)
WCDB_PROPERTY(content)
WCDB_PROPERTY(createTime)
WCDB_PROPERTY(modifiedTime)
@end
//Message.mm
@implementation Message
WCDB_IMPLEMENTATION(Message)
WCDB_SYNTHESIZE(Message, localID)
WCDB_SYNTHESIZE(Message, content)
WCDB_SYNTHESIZE(Message, createTime)
WCDB_SYNTHESIZE_COLUMN(Message, modifiedTime, "db_modifiedTime")
WCDB_PRIMARY_AUTO_INCREMENT(Message, localID)
WCDB_INDEX(Message, "_index", createTime)
@end
其中:
WCDB_IMPLEMENTATION(className)用于定義進(jìn)行綁定的類
WCDB_PROPERTY(propertyName)和WCDB_SYNTHESIZE(className, propertyName)用于聲明和定義字段。
WCDB_PRIMARY_AUTO_INCREMENT(className, propertyName)用于定義主鍵且自增。
WCDB_INDEX(className, indexNameSubfix, propertyName)用于定義索引。
雖然WCDB多了一步ORM的操作,但這是一勞永逸的,并且會給我們后續(xù)的使用帶來很大的便利。
經(jīng)過ORM的類,大部分操作都只需要一行代碼即可完成。
安裝
1 安裝CocoaPods.
2 Podfile里面寫 pod 'WCDB'
3 pod install
4 安裝好后編譯一下
- 注意: 由于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)建出分類.
-
command + n 彈出窗口,我們拉到下面,發(fā)現(xiàn)有WCDB一欄,選擇TableCodeable
屏幕快照 2018-04-02 下午7.33.08.png -
生成的一個(gè)model類和一個(gè)model類的分類
屏幕快照 2018-04-02 下午7.34.37.png
- SZYMessage.h文件
#import <Foundation/Foundation.h>
@interface SZYMessage : NSObject
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) NSInteger localID;
@property(nonatomic, assign) float totalScore;
@property(nonatomic, strong) NSDate *createDate;
@end
- SZYMessage.m文件
#import "SZYMessage+WCTTableCoding.h"
#import "SZYMessage.h"
#import <WCDB/WCDB.h>
@implementation SZYMessage
//WCDB_IMPLEMENTATION,用于在類文件中定義綁定到數(shù)據(jù)庫表的類
WCDB_IMPLEMENTATION(SZYMessage)
//WCDB_SYNTHESIZE,用于在類文件中定義綁定到數(shù)據(jù)庫表的字段
WCDB_SYNTHESIZE(SZYMessage, name)
WCDB_SYNTHESIZE(SZYMessage, localID)
//默認(rèn)使用屬性名作為數(shù)據(jù)庫表的字段名。對于屬性名與字段名不同的情況,可以使用WCDB_SYNTHESIZE_COLUMN(className, propertyName, columnName)進(jìn)行映射。
WCDB_SYNTHESIZE_COLUMN(SZYMessage, totalScore, "db_totalScore")
WCDB_SYNTHESIZE_DEFAULT(SZYMessage, createDate, WCTDefaultTypeCurrentDate) //設(shè)置一個(gè)默認(rèn)值
//主鍵
WCDB_PRIMARY_ASC_AUTO_INCREMENT(SZYMessage, localID)
//用于定義非空約束
WCDB_NOT_NULL(SZYMessage, name)
@end
- SZYMessage+WCTTableCoding.h 文件
#import "SZYMessage.h"
#import <WCDB/WCDB.h>
@interface SZYMessage (WCTTableCoding) <WCTTableCoding>
//WCDB_PROPERTY用于在頭文件中聲明綁定到數(shù)據(jù)庫表的字段,寫在分類里,不寫在.h里面,這樣view和controller不會 引入導(dǎo)入<WCDB/WCDB.h>的文件
WCDB_PROPERTY(name)
WCDB_PROPERTY(localID)
WCDB_PROPERTY(totalScore)
WCDB_PROPERTY(createDate)
@end
下面我們開始創(chuàng)建數(shù)據(jù)庫和表,并進(jìn)行增刪改查
##### 創(chuàng)建數(shù)據(jù)庫和表
- (BOOL)creatDatabaseAndTable {
//數(shù)據(jù)庫路徑
NSString *path = [self.baseDirectory stringByAppendingPathComponent:@"SampleDB"];
//NSLog(@"path--> %@",path);
//創(chuàng)建數(shù)據(jù)庫 路徑一樣, 該接口使用的是IF NOT EXISTS的SQL,因此可以用重復(fù)調(diào)用
WCTDatabase *database = [[WCTDatabase alloc] initWithPath:path];
_database = database;
if ([database canOpen]) {
NSLog(@"創(chuàng)建數(shù)據(jù)庫成功");
}else{
NSLog(@"創(chuàng)建數(shù)據(jù)庫失敗");
return NO;
}
//創(chuàng)建表 注:該接口使用的是IF NOT EXISTS的SQL,因此可以用重復(fù)調(diào)用。不需要在每次調(diào)用前判斷表或索引是否已經(jīng)存在。
BOOL result = [database createTableAndIndexesOfName:SZY_TABLE_MESSAGE_NAME withClass:SZYMessage.class];
if (!result) {
NSLog(@"創(chuàng)建表失敗");
return NO;
}
return YES;
}
##### 插入單個(gè)數(shù)據(jù)
- (BOOL)insertData:(SZYMessage *)message {
BOOL result = [_database insertObject:message into:SZY_TABLE_MESSAGE_NAME];
//關(guān)閉數(shù)據(jù)庫,_database如果能自己釋放的話,會自動(dòng)關(guān)閉,就不用手動(dòng)調(diào)用關(guān)閉了
[_database close];
if (!result) {
NSLog(@"插入失敗");
return NO;
}else{
NSLog(@"插入成功");
return YES;
}
}
//插入多個(gè)數(shù)據(jù):
BOOL result = [_database insertObject:message into:SZY_TABLE_MESSAGE_NAME];
//增刪改查用下面方法,可以鏈?zhǔn)秸{(diào)用
/*
WCTInsert
WCTDelete
WCTUpdate
WCTSelect
*/
WCTInsert *insert = [_database prepareInsertObjectsOfClass:SZYMessage.class
into:SZY_TABLE_MESSAGE_NAME];
BOOL result = [insert executeWithObjects:objects];
##### 查詢數(shù)據(jù) 用localID排序
- (void)selectOrder {
NSArray<SZYMessage *> *objects2 = [_database getObjectsOfClass:SZYMessage.class fromTable:SZY_TABLE_MESSAGE_NAME orderBy:SZYMessage.localID.order()];
[objects2 enumerateObjectsUsingBlock:^(SZYMessage *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"用localID排序 --> %@ ",obj);
}];
}
//查詢數(shù)據(jù) 指定范圍
- (void)selectCertainRange {
NSArray<SZYMessage *> *objects3 = [_database getObjectsOfClass:SZYMessage.class fromTable:SZY_TABLE_MESSAGE_NAME where:SZYMessage.localID.between(0,1) || SZYMessage.name.like(@"lil%")];
[objects3 enumerateObjectsUsingBlock:^(SZYMessage *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"objects3 --> %@ ",obj);
}];
}
//定向 將查詢的totalScore值賦給新創(chuàng)建的對象
- (void)selectAndAssignment {
SZYMessage *message5 = [_database getOneObjectOnResults:SZYMessage.totalScore.max().as(SZYMessage.totalScore) fromTable:SZY_TABLE_MESSAGE_NAME];
message5.localID = 5;
NSLog(@"message5 --> %@ ",message5);
}
//鏈?zhǔn)秸{(diào)用
- (void)selectChain {
//所有的對象
WCTSelect *select = [_database prepareSelectObjectsOfClass:SZYMessage.class fromTable:SZY_TABLE_MESSAGE_NAME ];
//鏈?zhǔn)讲樵? NSArray<SZYMessage *> *objects6 = [[select where:SZYMessage.totalScore < 90] limit:2].allObjects;
[objects6 enumerateObjectsUsingBlock:^(SZYMessage *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"objects6 --> %@ ",obj);
}];
}
##### 更新
- (void)updateData {
WCTUpdate *update = [_database prepareUpdateTable:SZY_TABLE_MESSAGE_NAME
onProperties:SZYMessage.name];
SZYMessage *object = [[SZYMessage alloc] init];
object.name = @"xiaoming22";
BOOL result = [update executeWithObject:object];
if (!result) {
NSLog(@"Update by object Error %@", update.error);
}else{
NSLog(@"更新成功");
}
}
//刪除表
- (void)deleteData {
WCTDelete *deletion = [_database prepareDeleteFromTable:SZY_TABLE_MESSAGE_NAME];
BOOL result = [deletion execute];
if (!result) {
NSLog(@"Delete Error %@", deletion.error);
}else{
NSLog(@"刪除成功");
}
[_database close];
//刪除name是xiaoming的人
// BOOL result = [_database deleteObjectsFromTable:SZY_TABLE_MESSAGE_NAME where:SZYMessage.name == @"xiaoming"];
// [_database deleteObjectsFromTable:SZY_TABLE_MESSAGE_NAME where:SZYMessage.localID.between(0,1) || SZYMessage.name.like(@"lil%")];
}
Transaction(事務(wù))
WCDB內(nèi)可通過兩種方式執(zhí)行Transaction(事務(wù)),一是runTransaction:接口
//事務(wù)
- (void)transactionUseBlock {
// blocked 方式用事務(wù)
BOOL committed = [_database runTransaction:^BOOL{
SZYMessage *object = [[SZYMessage alloc] init];
BOOL result = [_database insertObject:object
into:SZY_TABLE_MESSAGE_NAME];
//return YES to do a commit and return NO to do a rollback
if (result) {
return YES;
}
return NO;
} event:^(WCTTransactionEvent event) {
NSLog(@"Event %d", event);
}];
}
//不用block方式事務(wù)
- (void)transaction {
BOOL result = [_database beginTransaction];
SZYMessage *object = [[SZYMessage alloc] init];
result = [_database insertObject:object
into:SZY_TABLE_MESSAGE_NAME];
if (result) {
result = [_database commitTransaction];
} else {
result = [_database rollbackTransaction];
}
}
WINQ
上述例子中的一些特殊語法:
where:Message.localID>0
onProperties:Message.content
orderBy:Message.localID.order(WCTOrderedDescending) 這個(gè)便是WINQ。
WINQ(WCDB Integrated Query,音'wink'),即WCDB集成查詢,是將自然查詢的SQL集成到WCDB框架中的技術(shù),基于C++實(shí)現(xiàn)。
傳統(tǒng)的SQL語句,通常是開發(fā)者拼接字符串完成。這種方式不僅繁瑣、易錯(cuò),而且出錯(cuò)后很難定位到問題所在。同時(shí)也容易給SQL注入留下可乘之機(jī)。
下面是幾個(gè)官方文檔的例子


WINQ的接口包括但不限于:
一元操作符:+、-、!等
二元操作符:||、&&、+、-、*、/、|、&、<<、>>、<、<=、==、!=、>、>=等
范圍比較:IN、BETWEEN等
字符串匹配:LIKE、GLOB、MATCH、REGEXP等
聚合函數(shù):AVG、COUNT、MAX、MIN、SUM等
...
凡是SQLite支持的語法規(guī)則,WINQ基本都有其對應(yīng)的接口。且接口名稱與SQLite的語法規(guī)則基本保持一致。對于熟悉SQL的開發(fā)者,無須特別學(xué)習(xí)即可立刻上手使用。
鏈?zhǔn)秸{(diào)用
鏈?zhǔn)秸{(diào)用是指對象的接口返回一個(gè)對象,從而允許在單個(gè)語句中將調(diào)用鏈接在一起,而不需要變量來存儲中間結(jié)果。
WCDB對于增刪改查操作,都提供了對應(yīng)的類以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
WCTInsert
WCTDelete
WCTUpdate
WCTSelect
WCTRowSelect
WCTMultiSelect
where、orderBy、limit等接口的返回值均為self,因此可以通過鏈?zhǔn)秸{(diào)用,更自然更靈活的寫出對應(yīng)的查詢。


