iOS WCDB 傻瓜教程

這里只是提到了一些基礎是用法。深度用法,可以前往官網學習。 關于WCDB的背景和優缺點,這里也不做介紹,大家可以自行百度。

類字段綁定(ORM)

在WCDB內,ORM(Object Relational Mapping)是指
將一個ObjC的類,映射到數據庫的表和索引;
將類的property,映射到數據庫表的字段;
這一過程。通過ORM,可以達到直接通過Object進行數據庫操作,省去拼裝過程的目的。

WCDB通過內建的宏來實現ORM的功能。

首先創建一個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是結合c++寫的,引用#import <WCDB/WCDB.h>的文件.m里面都要改成.mm后綴的,所以一般上為了隔離model,不讓view喝viewController里面也改成.mm后綴的,我們寫一個model的分類,遵守WCTTableCoding協議并寫WCDB_PROPERTY(),WCDB編譯后項目里有快捷創建model類,直接創建出分類.

創建對應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

將一個已有的ObjC類進行ORM綁定的過程如下:

定義該類遵循WCTTableCoding協議。可以在類聲明上定義,也可以通過文件模版在category內定義。

  1. 使用WCDB_PROPERTY宏在頭文件聲明需要綁定到數據庫表的字段。

  2. 使用WCDB_IMPLEMENTATIO宏在類文件定義綁定到數據庫表的類。

  3. 使用WCDB_SYNTHESIZE宏在類文件定義需要綁定到數據庫表的字段。

簡單幾行代碼,就完成了將類和需要的字段綁定到數據庫表的過程。這三個宏在名稱和使用習慣上,也都和定義一個ObjC類相似,以此便于記憶。

除此之外,WCDB還提供了許多可選的宏,用于定義數據庫索引、約束等,如:

  1. WCDB_PRIMARY用于定義主鍵

  2. WCDB_INDEX用于定義索引

  3. WCDB_UNIQUE用于定義唯一約束

  4. WCDB_NOT_NULL用于定義非空約束

具體用法

在我們的項目中,定義好的接口(SEEDDBManagerProtocol) 可以滿足一般場景的增刪改查。

#import <WCDB/WCDB.h>

@protocol SEEDDBManagerProtocol<NSObject>

@required

//便利構造器
+ (instancetype)shareManager;


#pragma mark -- create table
/// 創建具體的表
/// @param tableName     表名
/// @param modelClass   對應的model的類
- (BOOL)createTableWithName:(NSString *)tableName
                 modelClass:(Class)modelClass;


@optional

#pragma mark -- insert

/// 在某個已知的表里,插入新單個數據
/// @param message        需要插入的數據
- (BOOL)insertData:(WCTObject *)message;

/// 在某個已知的表里,插入(已經存在該數據,就更新)新單個數據
/// @param message        需要插入的數據
- (BOOL)insertOrReplaceObject:(WCTObject *)message;

/// 在某個表里,插入單個數據
/// @param message        需要插入的數據
/// @param tableName    表名
/// @param modelClass  對應model的類名
- (BOOL)insertData:(WCTObject *)message
     withTableName:(NSString *)tableName
        modelClass:(Class)modelClass;

/// 在某個表里,插入(已經存在該數據,就更新)單個數據
/// @param message        需要插入的數據
/// @param tableName    表名
/// @param modelClass  對應model的類名
- (BOOL)insertOrReplaceObject:(WCTObject *)message
                withTableName:(NSString *)tableName
                   modelClass:(Class)modelClass;



#pragma mark -- update

/// 在某個已知的表里,更新草稿數據
/// @param message        需要更新的數據
- (BOOL)updateData:(WCTObject *)message;

/// 在某個表里,更新單個數據
/// @param tableName    表名
/// @param property      需要更新的屬性
/// @param value            對應的值
- (BOOL)updateAllRowsInTable:(NSString *)tableName
                  onProperty:(const WCTProperty &)property
                   withValue:(WCTValue *)value;;



#pragma mark -- select
/// 在某個已知的表里,根據條件獲取某個單獨的草稿數據
/// @param where        條件
- (WCTObject *)selectData:(const WCTCondition &)where;

/// 在某個表里,根據條件獲取某個單獨的草稿數據
/// @param where             條件
/// @param tableName    表名
/// @param modelClass  對應model的類
- (WCTObject *)selectData:(const WCTCondition &)where
            withTableName:(NSString *)tableName
               modelClass:(Class)modelClass;



#pragma mark -- delete
/// 在某個已知的表里,刪除某個數據
/// @param where        條件
- (BOOL)delegateData:(const WCTCondition &)where;

/// 在某個已知的表里,刪除某個數據
/// @param where             條件
/// @param tableName    表名
/// @param modelClass  對應model的類
- (BOOL)delegateDataWithWhere:(const WCTCondition &)where
                withTableName:(NSString *)tableName
                   modelClass:(Class)modelClass;


/**
    未完待續......
 */
@end

創建數據庫

#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];
}
//創建數據庫
- (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(@"創建數據庫成功");
    }else{
        NSLog(@"創建數據庫失敗");
        return NO;
    }
    
    return YES;
}

創建具體的表

//創建具體的表
- (BOOL) createTableWithName:(NSString *)tableName modelClass:(Class)modelClass{
    //創建表  注:該接口使用的是IF NOT EXISTS的SQL,因此可以用重復調用。不需要在每次調用前判斷表或索引是否已經存在。
    BOOL result = [self.database createTableAndIndexesOfName:tableName withClass:modelClass];
    
    if (!result) {
        NSLog(@"創建表失敗");
        return NO;
    }
    NSLog(@"創建表成功");
    return YES;
}

創建SEEDDBManager 的分類,來跟具體業務綁定。

#define kTable_Name @"TableName"
@implementation SEEDDBManager (businessOne)

/// 在某個已知的表里,插入(已經存在該數據,就更新)新單個數據
/// @param message        需要插入的數據
- (BOOL)insertOrReplaceObject:(Draft1 *)message{
    BOOL result = [self.database insertOrReplaceObject:message into:kTable_Name];
    
    //關閉數據庫,_database如果能自己釋放的話,會自動關閉,就不用手動調用關閉了
    [self.database close];
    if (!result) {
        NSLog(@"插入失敗");
        return NO;
    }else{
        NSLog(@"插入成功");
        return YES;
    }
}


/// 在某個已知的表里,根據條件獲取某個單獨的草稿數據
/// @param where        條件
- (WCTObject *)selectData:(const WCTCondition &)where{

    WCTTable *table = [self.database getTableOfName:kTable_Name withClass:Draft1.class];
    WCTObject *objc = [ table getOneObjectWhere:where];
    return objc;
}

/// 在某個已知的表里,刪除某個數據
/// @param message        需要插入的數據
- (BOOL)delegateData:(Draft1 *)message{
    //刪除
    //DELETE FROM message WHERE localID>0;
    BOOL result = [self.database deleteObjectsFromTable:kTable_Name
                                             where:Draft1.localID == message.localID];
    
    return result;
}

這樣可以使用全局統一的SEEDDBManager單利對象來調用數據庫方法。 具體的業務代碼則由各自SEEDDBManager的分類管理。

關于model的嵌套

在業務場景中,很容易遇到在Draft1模型的內部包含有一個其他的model。
不做任何處理的話,是沒法正常使用的。
WCDB提供了一些具體的類型和方法如下:


WCDB1.png
WCDB2.png

需要在內部嵌套的model類,做一些處理。 需要實現

  • (id)columnTypeForWCDB:
  • (instancetype)unarchiveWithWCTValue:(WCTValue *)value;
  • (id)archivedWCTValue;
    這三個方法。

創建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嵌套了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • @[TOC](IOS DB技術框架對比) 1. 數據庫簡介 目前移動端數據庫方案按其實現可分為兩類: 關系型數據庫...
    孔雨露閱讀 615評論 0 5
  • 基本特性 易用,WCDB支持一句代碼即可將數據取出并組合為object。 WINQ(WCDB語言集成查詢):通過W...
    AKyS佐毅閱讀 15,931評論 7 30
  • 前言 移動端的數據庫選型一直是一個難題,直到前段時間看到了WeMobileDev(微信前端團隊)放出了第三個開源組...
    上冬十二閱讀 3,136評論 1 5
  • 前言 WCDB是微信移動端團隊開源的移動端數據庫組件,提供了一個高效、完整、易用的移動端存儲方案。第一次應用到WC...
    沙琪瑪dd閱讀 21,739評論 3 18
  • 第一章 2 續 晨光熹微。潮濕的霧氣籠著蓮坂菜市場。市井百態間,有推著車賣早餐的小販,有賣海鮮趕早市的漁民,又有媽...
    尤婆姨lilly閱讀 182評論 0 2