微信数据存储WCDB for iOS/macOS

WCDB

WCDB是一个高效、完整、易用的移动数据库框架,基于SQLCipher,支持iOS, macOS。

WCDB for iOS/macOS

基本特性

  • 易用,WCDB支持一句代码即可将数据取出并组合为object。

    • WINQ(WCDB语言集成查询):通过WINQ,开发者无须为了拼接SQL的字符串而写一大坨胶水代码。

    • ORM(Object Relational Mapping):WCDB支持灵活、易用的ORM。开发者可以很便捷地定义表、索引、约束,并进行增删改查操作。

      [database getObjectsOfClass:WCTSampleConvenient.class
                        fromTable:tableName
                            where:WCTSampleConvenient.intValue>=10
                            limit:20];
  • 高效,WCDB通过框架层和sqlcipher源码优化,使其更高效的表现。

    • 多线程高并发:WCDB支持多线程读与读、读与写并发执行,写与写串行执行。

    • 批量写操作性能测试:

      更多关于WCDB的性能数据,请参考benchmark

  • 完整,WCDB覆盖了数据库相关各种场景的所需功能。

    • 加密:WCDB提供基于SQLCipher的数据库加密。
    • 损坏修复:WCDB内建了Repair Kit用于修复损坏的数据库。
    • 反注入:WCDB内建了对SQL注入的保护。

基本要求

  • WCDB支持iOS 7、macOS 10.9以上。
  • WCDB需使用Xcode 8.0以上版本进行编译。
  • 需使用Objective-C++。

 

通过CocoaPods安装,此处不介绍安装过程,感兴趣可以参考文章:http://www.cnblogs.com/HJiang/p/7228166.html

 

/*
 将一个已有的ObjC类进行ORM绑定的过程如下:
 
 定义该类遵循WCTTableCoding协议。可以在类声明上定义,也可以通过文件模版在category内定义。
 使用WCDB_PROPERTY宏在头文件声明需要绑定到数据库表的字段。
 使用WCDB_IMPLEMENTATIO宏在类文件定义绑定到数据库表的类。
 使用WCDB_SYNTHESIZE宏在类文件定义需要绑定到数据库表的字段。
 */

.实体类.

新建Message类

Message.h

#import <Foundation/Foundation.h>

@interface Message : NSObject

/**
 * 本地id
 */
@property (nonatomic,assign) int localID;

/**
 *  消息内容
 */
@property (nonatomic, strong) NSString *content;

/**
 *  创建时间
 */
@property (nonatomic, strong) NSDate *createTime;

/**
 *  最后更新时间
 */
@property (nonatomic, strong) NSDate *modifiedTime;

/**
 *  未读消息
 */
@property (nonatomic,assign) int unused;

@end

Message.mm

#import "Message.h"
#import "Message+WCTTableCoding.h"

@implementation Message

// 利用这个宏定义绑定到表的类
WCDB_IMPLEMENTATION(Message)

// 下面四个宏定义绑定到表中的字段
WCDB_SYNTHESIZE(Message, localID)
WCDB_SYNTHESIZE(Message, content)
WCDB_SYNTHESIZE(Message, createTime)
WCDB_SYNTHESIZE(Message, modifiedTime)

// 约束宏定义数据库的主键
WCDB_PRIMARY(Message, localID)

// 定义数据库的索引属性,它直接定义createTime字段为索引
// 同时 WCDB 会将表名 + "_index" 作为该索引的名称
WCDB_INDEX(Message, "_index", createTime)


- (NSString *)description{
    return [NSString stringWithFormat:@"localID:%d content:%@ createTime:%@ modifiedTime:%@",_localID,_content,_createTime,_modifiedTime];
}

@end

Message+WCTTableCoding.h Message分类

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

@interface Message (WCTTableCoding) <WCTTableCoding>

/*
 需要绑定到表中的字段在这里声明,在.mm中去绑定
 使用WCDB_PROPERTY宏在头文件声明需要绑定到数据库表的字段。
 */
WCDB_PROPERTY(localID)
WCDB_PROPERTY(content)
WCDB_PROPERTY(createTime)
WCDB_PROPERTY(modifiedTime)

@end

由于WCDB是基于Objective-C++,因此需要将引用WCDB的源文件后缀.m改为.mm

所以Message.m需修改为.mm

数据库管理类

WCTDatabaseManager.h WCTDatabaseManager.mm

#import <Foundation/Foundation.h>
@class WCTDatabase;

@interface WCTDatabaseManager : NSObject

+ (instancetype)shareInstance;

- (WCTDatabase *)getDatabase;

/**
 创建数据库
 
 @param tableName 表名称
 @return 是否创建成功
 */
- (BOOL)creatDataBaseWithName:(NSString *)tableName;

@end
#import "WCTDatabaseManager.h"
#import "WCTDatabaseManager+DataBase.h"
#import "Message.h"
//#import "Message+WCTTableCoding.h"

@interface WCTDatabaseManager()
{
    WCTDatabase *database;
}

@end

@implementation WCTDatabaseManager

+ (instancetype)shareInstance{
    
    static WCTDatabaseManager * instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        instance = [[WCTDatabaseManager alloc]init];
        
    });
    
    return instance;
}

- (NSString *)getDatabasePath{
    //获取沙盒根目录
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    // 文件路径
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"Wechat.sqlite"];
    return filePath;
}

- (WCTDatabase *)getDatabase{
    if(!database){
        NSString *filePath = [self getDatabasePath];
        NSLog(@"wChatDatapath = %@",filePath);
        
        database = [[WCTDatabase alloc] initWithPath:filePath];
    }
    return database;
}

-(BOOL)creatDataBaseWithName:(NSString *)tableName{
    
    [self getDatabase];
    
    // 数据库加密
    //NSData *password = [@"MyPassword" dataUsingEncoding:NSASCIIStringEncoding];
    //[database setCipherKey:password];
    //测试数据库是否能够打开
    if ([database canOpen]) {
        
        // WCDB大量使用延迟初始化(Lazy initialization)的方式管理对象,因此SQLite连接会在第一次被访问时被打开。开发者不需要手动打开数据库。
        // 先判断表是不是已经存在
        if ([database isOpened]) {
            if ([database isTableExists:tableName]) {
                NSLog(@"表已经存在");
                return NO;
            }else{
                return [database createTableAndIndexesOfName:tableName withClass:Message.class];
            }
        }
    }
    return NO;
}

@end

WCTDatabaseManager+DataBase.h WCTDatabaseManager分类,引入WCDB头文件

#import <WCDB/WCDB.h>

@interface WCTDatabaseManager (DataBase)

@end

数据库操作类

#import <Foundation/Foundation.h>
@class Message;

@interface MessageDao : NSObject

- (BOOL)insertMessage:(Message *)message;

- (BOOL)insertMessageWithTransaction:(Message *)message;

- (BOOL)insertMessageWithBlock:(Message *)message;

- (BOOL)deleteMessage:(Message *)message;

- (BOOL)updataMessage:(Message *)message;

- (NSArray *)seleteMessages;

@end
#import "MessageDao.h"
#import "WCTDatabaseManager.h"
#import <WCDB/WCDB.h>
#import "Message.h"
#import "Message+WCTTableCoding.h"

#define kDataBase [[WCTDatabaseManager shareInstance] getDatabase]

@implementation MessageDao

- (BOOL)insertMessage:(Message *)message{
    return  [kDataBase insertObject:message into:@"message"];
}

// WCTDatabase 事务操作,利用WCTTransaction
- (BOOL)insertMessageWithTransaction:(Message *)message{

    BOOL ret = [kDataBase beginTransaction];
    ret = [self insertMessage:message];
    if (ret) {
        [kDataBase commitTransaction];
    }else{
        [kDataBase rollbackTransaction];
    }
    return ret;
}


// 另一种事务处理方法Block
- (BOOL)insertMessageWithBlock:(Message *)message{

    BOOL commit = [kDataBase runTransaction:^BOOL{
        BOOL ret = [self insertMessage:message];
        if (ret) {
            return YES;
        }else{
            return NO;
        }
    } event:^(WCTTransactionEvent event) {
        NSLog(@"Event %d", event);
    }];
    return commit;
}

- (BOOL)deleteMessage:(Message *)message{
//    删除
    //DELETE FROM message WHERE localID>0;
    return [kDataBase deleteObjectsFromTable:@"message" where:Message.localID > 0];
}

- (BOOL)updataMessage:(Message *)message{
    //修改
    //UPDATE message SET content="Hello, Wechat!";
    return [kDataBase updateAllRowsInTable:@"message" onProperty:Message.content withObject:message];
}

- (NSArray *)seleteMessage{
    
    //SELECT * FROM message ORDER BY localID
    NSArray<Message *> * message = [kDataBase getObjectsOfClass:Message.class fromTable:@"message" orderBy:Message.localID.order()];
    
    return message;
}

@end

测试方法

---恢复内容结束---

WCDB

WCDB是一个高效、完整、易用的移动数据库框架,基于SQLCipher,支持iOS, macOS。

WCDB for iOS/macOS

基本特性

  • 易用,WCDB支持一句代码即可将数据取出并组合为object。

    • WINQ(WCDB语言集成查询):通过WINQ,开发者无须为了拼接SQL的字符串而写一大坨胶水代码。

    • ORM(Object Relational Mapping):WCDB支持灵活、易用的ORM。开发者可以很便捷地定义表、索引、约束,并进行增删改查操作。

      [database getObjectsOfClass:WCTSampleConvenient.class
                        fromTable:tableName
                            where:WCTSampleConvenient.intValue>=10
                            limit:20];
  • 高效,WCDB通过框架层和sqlcipher源码优化,使其更高效的表现。

    • 多线程高并发:WCDB支持多线程读与读、读与写并发执行,写与写串行执行。

    • 批量写操作性能测试:

      更多关于WCDB的性能数据,请参考benchmark

  • 完整,WCDB覆盖了数据库相关各种场景的所需功能。

    • 加密:WCDB提供基于SQLCipher的数据库加密。
    • 损坏修复:WCDB内建了Repair Kit用于修复损坏的数据库。
    • 反注入:WCDB内建了对SQL注入的保护。

基本要求

  • WCDB支持iOS 7、macOS 10.9以上。
  • WCDB需使用Xcode 8.0以上版本进行编译。
  • 需使用Objective-C++。

 

通过CocoaPods安装,此处不介绍安装过程,感兴趣可以参考文章:http://www.cnblogs.com/HJiang/p/7228166.html

/*

 将一个已有的ObjC类进行ORM绑定的过程如下:

 

 定义该类遵循WCTTableCoding协议。可以在类声明上定义,也可以通过文件模版在category内定义。

 使用WCDB_PROPERTY宏在头文件声明需要绑定到数据库表的字段。

 使用WCDB_IMPLEMENTATIO宏在类文件定义绑定到数据库表的类。

 使用WCDB_SYNTHESIZE宏在类文件定义需要绑定到数据库表的字段。

 */

以例子参考:

.实体类.

新建Message类

Message.h

#import <Foundation/Foundation.h>

@interface Message : NSObject

/**
 * 本地id
 */
@property (nonatomic,assign) int localID;

/**
 *  消息内容
 */
@property (nonatomic, strong) NSString *content;

/**
 *  创建时间
 */
@property (nonatomic, strong) NSDate *createTime;

/**
 *  最后更新时间
 */
@property (nonatomic, strong) NSDate *modifiedTime;

/**
 *  未读消息
 */
@property (nonatomic,assign) int unused;

@end

Message.mm

#import "Message.h"
#import "Message+WCTTableCoding.h"

@implementation Message

// 利用这个宏定义绑定到表的类
WCDB_IMPLEMENTATION(Message)

// 下面四个宏定义绑定到表中的字段
WCDB_SYNTHESIZE(Message, localID)
WCDB_SYNTHESIZE(Message, content)
WCDB_SYNTHESIZE(Message, createTime)
WCDB_SYNTHESIZE(Message, modifiedTime)

// 约束宏定义数据库的主键
WCDB_PRIMARY(Message, localID)

// 定义数据库的索引属性,它直接定义createTime字段为索引
// 同时 WCDB 会将表名 + "_index" 作为该索引的名称
WCDB_INDEX(Message, "_index", createTime)


- (NSString *)description{
    return [NSString stringWithFormat:@"localID:%d content:%@ createTime:%@ modifiedTime:%@",_localID,_content,_createTime,_modifiedTime];
}

@end

Message+WCTTableCoding.h Message分类

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

@interface Message (WCTTableCoding) <WCTTableCoding>

/*
 需要绑定到表中的字段在这里声明,在.mm中去绑定
 使用WCDB_PROPERTY宏在头文件声明需要绑定到数据库表的字段。
 */
WCDB_PROPERTY(localID)
WCDB_PROPERTY(content)
WCDB_PROPERTY(createTime)
WCDB_PROPERTY(modifiedTime)

@end

由于WCDB是基于Objective-C++,因此需要将引用WCDB的源文件后缀.m改为.mm

所以Message.m需修改为.mm

数据库管理类

WCTDatabaseManager.h WCTDatabaseManager.mm

#import <Foundation/Foundation.h>
@class WCTDatabase;

@interface WCTDatabaseManager : NSObject

+ (instancetype)shareInstance;

- (WCTDatabase *)getDatabase;

/**
 创建数据库
 
 @param tableName 表名称
 @return 是否创建成功
 */
- (BOOL)creatDataBaseWithName:(NSString *)tableName;

@end
#import "WCTDatabaseManager.h"
#import "WCTDatabaseManager+DataBase.h"
#import "Message.h"
//#import "Message+WCTTableCoding.h"

@interface WCTDatabaseManager()
{
    WCTDatabase *database;
}

@end

@implementation WCTDatabaseManager

+ (instancetype)shareInstance{
    
    static WCTDatabaseManager * instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        instance = [[WCTDatabaseManager alloc]init];
        
    });
    
    return instance;
}

- (NSString *)getDatabasePath{
    //获取沙盒根目录
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    // 文件路径
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"Wechat.sqlite"];
    return filePath;
}

- (WCTDatabase *)getDatabase{
    if(!database){
        NSString *filePath = [self getDatabasePath];
        NSLog(@"wChatDatapath = %@",filePath);
        
        database = [[WCTDatabase alloc] initWithPath:filePath];
    }
    return database;
}

-(BOOL)creatDataBaseWithName:(NSString *)tableName{
    
    [self getDatabase];
    
    // 数据库加密
    //NSData *password = [@"MyPassword" dataUsingEncoding:NSASCIIStringEncoding];
    //[database setCipherKey:password];
    //测试数据库是否能够打开
    if ([database canOpen]) {
        
        // WCDB大量使用延迟初始化(Lazy initialization)的方式管理对象,因此SQLite连接会在第一次被访问时被打开。开发者不需要手动打开数据库。
        // 先判断表是不是已经存在
        if ([database isOpened]) {
            if ([database isTableExists:tableName]) {
                NSLog(@"表已经存在");
                return NO;
            }else{
                return [database createTableAndIndexesOfName:tableName withClass:Message.class];
            }
        }
    }
    return NO;
}

@end

WCTDatabaseManager+DataBase.h WCTDatabaseManager分类,引入WCDB头文件

#import <WCDB/WCDB.h>

@interface WCTDatabaseManager (DataBase)

@end

数据库操作类

#import <Foundation/Foundation.h>
@class Message;

@interface MessageDao : NSObject

- (BOOL)insertMessage:(Message *)message;

- (BOOL)insertMessageWithTransaction:(Message *)message;

- (BOOL)insertMessageWithBlock:(Message *)message;

- (BOOL)deleteMessage:(Message *)message;

- (BOOL)updataMessage:(Message *)message;

- (NSArray *)seleteMessages;

@end
#import "MessageDao.h"
#import "WCTDatabaseManager.h"
#import <WCDB/WCDB.h>
#import "Message.h"
#import "Message+WCTTableCoding.h"

#define kDataBase [[WCTDatabaseManager shareInstance] getDatabase]

@implementation MessageDao

- (BOOL)insertMessage:(Message *)message{
    return  [kDataBase insertObject:message into:@"message"];
}

// WCTDatabase 事务操作,利用WCTTransaction
- (BOOL)insertMessageWithTransaction:(Message *)message{

    BOOL ret = [kDataBase beginTransaction];
    ret = [self insertMessage:message];
    if (ret) {
        [kDataBase commitTransaction];
    }else{
        [kDataBase rollbackTransaction];
    }
    return ret;
}


// 另一种事务处理方法Block
- (BOOL)insertMessageWithBlock:(Message *)message{

    BOOL commit = [kDataBase runTransaction:^BOOL{
        BOOL ret = [self insertMessage:message];
        if (ret) {
            return YES;
        }else{
            return NO;
        }
    } event:^(WCTTransactionEvent event) {
        NSLog(@"Event %d", event);
    }];
    return commit;
}

- (BOOL)deleteMessage:(Message *)message{
//    删除
    //DELETE FROM message WHERE localID>0;
    return [kDataBase deleteObjectsFromTable:@"message" where:Message.localID > 0];
}

- (BOOL)updataMessage:(Message *)message{
    //修改
    //UPDATE message SET content="Hello, Wechat!";
    return [kDataBase updateAllRowsInTable:@"message" onProperty:Message.content withObject:message];
}

- (NSArray *)seleteMessage{
    
    //SELECT * FROM message ORDER BY localID
    NSArray<Message *> * message = [kDataBase getObjectsOfClass:Message.class fromTable:@"message" orderBy:Message.localID.order()];
    
    return message;
}

@end

测试方法

#import "ViewController.h"
#import "WCTDatabaseManager.h"
#import "MessageDao.h"
#import "Message.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (IBAction)createDataBaseDidClick:(UIButton *)sender {
    
    BOOL  result = [[WCTDatabaseManager shareInstance] creatDataBaseWithName:@"message"];
    NSLog(@"%@",((result == YES)?@"创建数据库成功":@"创建数据库失败"));
}

- (IBAction)insertBtnDidClick:(UIButton *)sender {
    MessageDao *messageDao = [[MessageDao alloc] init];
    Message *message = [[Message alloc] init];
    message.localID = 1;
    message.content = @"Hello, WCDB!";
    message.createTime = [NSDate date];
    message.modifiedTime = [NSDate date];
    [messageDao insertMessageWithTransaction:message];
}

- (IBAction)updateBtnDidClick:(UIButton *)sender {
    MessageDao *messageDao = [[MessageDao alloc] init];
    Message *message = [[Message alloc] init];
    message.content = @"Hello, Wechat!";
    [messageDao updataMessage:message];
}

- (IBAction)selectBtnDidClick:(UIButton *)sender {
    MessageDao *messageDao = [[MessageDao alloc] init];
    NSArray *messages = [messageDao seleteMessage];
    NSLog(@"%@",messages);
}

- (IBAction)deleteBtnDidClick:(UIButton *)sender {
    MessageDao *messageDao = [[MessageDao alloc] init];
    [messageDao deleteMessage:nil];
}


@end

更多的查询操作后续更新.

原文地址:https://www.cnblogs.com/HJiang/p/8260628.html