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安装,此处不介绍安装过程,感兴趣可以参考文章:
/*
将一个已有的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安装,此处不介绍安装过程,感兴趣可以参考文章:
/*
将一个已有的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
更多的查询操作后续更新.