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

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

  • 相关阅读:
    vue项目中使用定时器,离开页面时清除定时器
    不能在循环中使用res.send(err);
    React使用require加载图片失败
    实验五 单元测试
    实验四 代码评审
    UML 建模工具的安装与使用
    结对编程 第二阶段
    结对编程
    GIT 代码版本管理
    结构化方法与面向对象化方法的比较
  • 原文地址:https://www.cnblogs.com/HJiang/p/8260628.html
Copyright © 2011-2022 走看看