zoukankan      html  css  js  c++  java
  • 微信数据存储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

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

  • 相关阅读:
    Chapter 03Using SingleRow Functions to Customize Output(03)
    Chapter 03Using SingleRow Functions to Customize Output(01)
    Chapter 04Using Conversion Functions and Conditional ExpressionsNesting Functions
    Chapter 04Using Conversion Functions and Conditional ExpressionsGeneral Functions
    Chapter 11Creating Other Schema Objects Index
    传奇程序员John Carmack 访谈实录 (zz.is2120)
    保持简单纪念丹尼斯里奇(Dennis Ritchie) (zz.is2120.BG57IV3)
    王江民:传奇一生 (zz.is2120)
    2011台湾游日月潭
    2011台湾游星云大师的佛光寺
  • 原文地址:https://www.cnblogs.com/HJiang/p/8260628.html
Copyright © 2011-2022 走看看