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 
@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 的文件.m里面都要改成.mm后缀的,所以一般上为了隔离model,不让view喝viewController里面也改成.mm后缀的,我们写一个model的分类,遵守WCTTableCoding协议并写WCDB_PROPERTY(),WCDB编译后项目里有快捷创建model类,直接创建出分类.

创建对应model的分类

.h
#import 

NS_ASSUME_NONNULL_BEGIN

@interface Draft1 (wcdb)

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 

@protocol SEEDDBManagerProtocol

@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 

@interface DetailModel : NSObject

@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

这样就可以model嵌套了。

你可能感兴趣的:(iOS WCDB 傻瓜教程)