前言

首先写这篇文章之前祝大家元旦快乐,然后自我介绍一下,我叫吴海超(WHC)在iOS领域有丰富的开发架构经验Github以后我也会以文章的形式分享具有实战意义的文章给大家,希望能够给大家有所帮助。

主题

好今天这篇文章我主要给大家讲讲Sqlite应用,我想大家应该都知道怎么去应用以及使用Sqlite(创建数据库,建表,增删改查。。。),而且这些步骤现在都已经是固定模式了,既然都已经是固定模式了那我们可不可以对这些固定模式操作进行一层封装让使用更方便高效?答案是那是肯定可以的,这个具体怎么封装稍后再讨论,我们先看看传统使用Sqlite方式。

传统方式使用Sqlite

  • 创建数据库
sqlite3 *database;
- (void)createSqlite {NSArray *documentArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentPath = [documentArray firstObject];// 新建名称为whc.sqlite 数据文件存放在沙箱doc目录下面 NSString *path = [NSString stringWithFormat:@"%@/whc.sqlite",documentPath];/// 创建并且打开数据库sqlite3_open([[self path] UTF8String], &database);
}复制代码
  • 建表
- (void)createTable {const char *createSQL = "create table if not exists whc(id integer primary key autoincrement,name char,sex char)";sqlite3_exec(database, createSQL, NULL, NULL, NULL);
}复制代码
  • 增删改查
//<1> 插入记录Sql: "insert into whc (name,sex)values('whc','male')"
//<2> 删除记录Sql: "delete from whc where ...."
//<3> 修改记录Sql: "update whc set ... where ...."
//<4> 查询记录Sql: "select from whc ......"
//执行sql:
sqlite3_stmt * stmt;
/// 预执行Sql
sqlite3_prepare_v2(database, sql, -1, &stmt, nil);
/// 执行sql
sqlite3_step(stmt);
....
处理结果,然后关闭数据库复制代码

对传统方式分析

我可以看到这上面的步骤是我们可以经常看到的方式,虽然上面的精简了很多但是我认为还是很无聊,每创建一个数据库都要这样搞一遍效率实在很低而且不可靠一点都不灵活,其实大家都知道的CoreData也是很固定模式的,今年5月初哦不对现在是2017年了应该是去年5月,我就思考能不能一行代码操作数据库呢?我下班在回去的公交车上思前想后觉得完全可行的,后来我的方案是:[Runtime + Sqlite]技术架构。

第二天就开始实施尝试经过不断调试最后第一版本开发出来了WHC_ModelSqlite下面我看看怎么来一行代码操作Sqlite

WHC_ModelSqlite介绍

  • 目标: 替代直接使用Sqlite和CoreData
  • 架构: 线程安全,采用runtime技术和Sqlite Api完美结合打造
  • 易用: 告别繁琐sql语句的编写和CoreData复杂创建
  • 支持: (NSData,NSString,Int,double,float,Bool,char,NSNumber)类型
  • 强大: 支持模型嵌套模型类存储到数据库和多表嵌套联查
  • 智能: 智能根据数据库模型类提供的VERSION方法返回的版本号动态更新数据库字段(动态删除/添加)

先看看架构图:

首先WHC_ModelSqlite是不需要显示的去创建数据库和表而是通过Model类来决定数据库名称和表名以及字段名称和数据类型,好下面先做一个使用演示:
假设有这样一个模型Person类对象whc

Person * whc = [Person new];
whc.name = @"吴海超";
whc.age = 25;
whc.height = 180.0;
whc.weight = 140.0;
whc.isDeveloper = YES;
whc.sex = 'm';// 嵌套car对象
whc.car = [Car new];
whc.car.name = @"撼路者";
whc.car.brand = @"大路虎";// 嵌套school对象
whc.school = [School new];
whc.school.name = @"北京大学";
whc.school.personCount = 5000;// school对象嵌套city对象
whc.school.city = [City new];
whc.school.city.name = @"北京";
whc.school.city.personCount = 1000;复制代码

我们怎么把person插入到数据库呢?看下面

/// 把上面创建好的whc对象插入数据库
[WHC_ModelSqlite insert:whc];复制代码

可能很多人在这里会有疑问?怎么没有创建数据库和表以及字段呢?首先WHC_ModelSqlite上面介绍以及说了是不需要这样操作的,为什么?那是因为WHC_ModelSqlite是基于Runtime技术开发的在上面执行[WHC_ModelSqlite insert:whc]时候会检查模型Person类是否已经创建了数据库,如果没有会为Person模型类创建,然后通过Runtime读取Person的属性名称以及类型然后通过类型映射把oc数据类型转换为Sqlite字段类型并且创建表(表名就是model类名),好上面对创建数据库表以及字段做了分析,那么whc对象是如何插入到数据库的?
在上面的检查工作都好做了insert会用runtime技术获取model对象whc所有属性名称类型以及对应的值,然后通过前面获取的属性名称和值拼接Sql插入语句然后再执行的,对没错步骤就是这样。然后过程看起来好像很简单但其实有很多细节要做控制,具体看参考源代码
插入代码片段

+ (void)insert:(id)model_object {/// 如果当前有其他数据库的操作那么等待dispatch_semaphore_wait([self shareInstance].dsema, DISPATCH_TIME_FOREVER);@autoreleasepool {[[self shareInstance].sub_model_info removeAllObjects];[self insertModelObject:model_object];}dispatch_semaphore_signal([self shareInstance].dsema);
}/// 处理插入模型对象
+ (sqlite_int64)insertModelObject:(id)model_object {/// 扫描model对象的详细信息(runtime)NSDictionary * sub_model_objects_info = [self scanSubModelObject:model_object];if (sub_model_objects_info.count > 0) {[sub_model_objects_info enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {/// 分析处理model对象的子model对象sqlite_int64 _id = [self insertModelObject:obj];[[self shareInstance].sub_model_info setObject:@(_id) forKey:key];}];}/// 插入model对象并且返回_idreturn [self commonInsertSubModelObject:model_object];
}+ (sqlite_int64)commonInsertSubModelObject:(id)sub_model_object {sqlite_int64 _id = -1;/// 创建表以及打开表创建字段if ([self openTable:[sub_model_object class]]) {/// 开启事务[self execSql:@"BEIGIN"];/// 插入model对象[self commonInsert:sub_model_object index:-1];/// 提交事务[self execSql:@"COMMIT"];/// 获取表最大的id并返回_id = [self getModelMaxIdWithClass:[sub_model_object class]];/// 关闭数据库[self close];}return _id;
}/// 执行插入操作
+ (void)commonInsert:(id)model_object index:(NSInteger)index {sqlite3_stmt * pp_stmt = nil;/// 分析model信息(属性名称/类型)NSDictionary * field_dictionary = [self parserModelObjectFieldsWithModelClass:[model_object class]];/// 获取表名NSString * table_name = NSStringFromClass([model_object class]);/// 下面都是构建sql insert插入语句模块__block NSString * insert_sql = [NSString stringWithFormat:@"INSERT INTO %@ (",table_name];NSArray * field_array = field_dictionary.allKeys;NSMutableArray * value_array = [NSMutableArray array];NSMutableArray * insert_field_array = [NSMutableArray array];[field_array enumerateObjectsUsingBlock:^(NSString *  _Nonnull field, NSUInteger idx, BOOL * _Nonnull stop) {WHC_PropertyInfo * property_info = field_dictionary[field];[insert_field_array addObject:field];insert_sql = [insert_sql stringByAppendingFormat:@"%@,",field];id value = [model_object valueForKey:field];id subModelKeyId = [self shareInstance].sub_model_info[property_info.name];if ((value && subModelKeyId == nil) || index == _NO_HANDLE_KEY_ID) {[value_array addObject:value];}else {switch (property_info.type) {case _Data: {[value_array addObject:[NSData data]];}break;case _String: {[value_array addObject:@""];}break;case _Number: {[value_array addObject:@(0.0)];}break;case _Model: {if ([subModelKeyId isKindOfClass:[NSArray class]]) {[value_array addObject:subModelKeyId[index]];}else {if (subModelKeyId) {[value_array addObject:subModelKeyId];}else {[value_array addObject:@(_NO_HANDLE_KEY_ID)];}}}break;case _Int: {id sub_model_main_key_object = [self shareInstance].sub_model_info[property_info.name];if (sub_model_main_key_object != nil) {if (index != -1) {[value_array addObject:sub_model_main_key_object[index]];}else {[value_array addObject:sub_model_main_key_object];}}else {NSNumber * value = @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model_object, property_info.getter));[value_array addObject:value];}}break;case _Boolean: {NSNumber * value = @(((Boolean (*)(id, SEL))(void *) objc_msgSend)((id)model_object, property_info.getter));[value_array addObject:value];}break;case _Char: {NSNumber * value = @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model_object, property_info.getter));[value_array addObject:value];}break;case _Double: {NSNumber * value = @(((double (*)(id, SEL))(void *) objc_msgSend)((id)model_object, property_info.getter));[value_array addObject:value];}break;case _Float: {NSNumber * value = @(((float (*)(id, SEL))(void *) objc_msgSend)((id)model_object, property_info.getter));[value_array addObject:value];}break;default:break;}}}];insert_sql = [insert_sql substringWithRange:NSMakeRange(0, insert_sql.length - 1)];insert_sql = [insert_sql stringByAppendingString:@") VALUES ("];[field_array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {insert_sql = [insert_sql stringByAppendingString:@"?,"];}];insert_sql = [insert_sql substringWithRange:NSMakeRange(0, insert_sql.length - 1)];insert_sql = [insert_sql stringByAppendingString:@")"];/// 准备执行插入sqlif (sqlite3_prepare_v2(_whc_database, [insert_sql UTF8String], -1, &pp_stmt, nil) == SQLITE_OK) {[field_array enumerateObjectsUsingBlock:^(NSString *  _Nonnull field, NSUInteger idx, BOOL * _Nonnull stop) {/// 进行值得绑定WHC_PropertyInfo * property_info = field_dictionary[field];id value = value_array[idx];int index = (int)[insert_field_array indexOfObject:field] + 1;switch (property_info.type) {case _Data:sqlite3_bind_blob(pp_stmt, index, [value bytes], (int)[value length], SQLITE_TRANSIENT);break;case _String:sqlite3_bind_text(pp_stmt, index, [value UTF8String], -1, SQLITE_TRANSIENT);break;case _Number:sqlite3_bind_double(pp_stmt, index, [value doubleValue]);break;case _Model:sqlite3_bind_int64(pp_stmt, index, (sqlite3_int64)[value integerValue]);break;case _Int:sqlite3_bind_int64(pp_stmt, index, (sqlite3_int64)[value longLongValue]);break;case _Boolean:sqlite3_bind_int(pp_stmt, index, [value boolValue]);break;case _Char:sqlite3_bind_int(pp_stmt, index, [value intValue]);break;case _Float:sqlite3_bind_double(pp_stmt, index, [value floatValue]);break;case _Double:sqlite3_bind_double(pp_stmt, index, [value doubleValue]);break;default:break;}}];if (sqlite3_step(pp_stmt) != SQLITE_DONE) {sqlite3_finalize(pp_stmt);}}else {[self log:@"Sorry存储数据失败,建议检查模型类属性类型是否符合规范"];}
}复制代码

好上面对开源库WHC_ModelSqlite的插入操作做了详细分析,由于篇幅限制下面我其他操作简要进行演示。

查询

/// 查询所有记录
NSArray * personArray = [WHC_ModelSqlite query:[Person class]];
/// 查询name字段为whc并且age等于25的小伙子
[WHC_ModelSqlite query:[Person class] where:@"name = 'whc' and age = 25"];
///对person数据表查询并且根据age自动降序或者升序排序
[WHC_ModelSqlite query:[Person class] order:@"by age desc/asc"];
/// 对person数据表查询并且并且限制查询数量为8
[WHC_ModelSqlite query:[Person class] limit:@"8"];
/// 对person数据表查询并且对查询列表偏移8并且限制查询数量为8
[WHC_ModelSqlite query:[Person class] limit:@"8 offset 8"];复制代码

上面api具体代码实现可以参考源代码

更新

/// 把记录name等于whc或者age小于等于30的更新为现在的模型whc对象
[WHC_ModelSqlite update:whc where:@"name = 'whc' OR age <= 30"];复制代码

有没有很先进。。。。。perfect

删除

/// 删除记录age等于25并且name等于whc的记录
[WHC_ModelSqlite delete:[Person class] where:@"age = 25 AND name = 'whc'"];复制代码

清空

/// 清空数据库Person所有记录
[WHC_ModelSqlite clear:[Person class]];复制代码

删除数据库

/// 删除Person数据库
[WHC_ModelSqlite removeModel:[Person class]];复制代码

删除所有数据库

/// 删除所有模型数据库
[WHC_ModelSqlite removeAllModel];复制代码

获取数据库本地路径

NSString * path = [WHC_ModelSqlite localPathWithModel:[Person class]];复制代码

获取数据库本地版本号

NSString * path = [WHC_ModelSqlite versionWithModel:[Person class]];复制代码

注意

当模型类有新增/删除属性的时候需要在模型类里定义类方法(+ (NSString*)VERSION)修改模型类(数据库)版本号来表明有字段更新操作,库会根据这个VERSION变更智能检查并自动更新数据库字段,无需手动更新数据库字段。

结束

  • 如果您在使用过程中有任何问题,欢迎issue me! - 很乐意为您解答任何相关问题!
  • 与其给我点star,不如向我狠狠地抛来一个BUG!
  • 如果您想要更多的接口来自定义或者建议/意见,欢迎issue me!我会根据大家的需求提供更多的接口!

谢谢您的阅读,WHC_ModelSqlite开源地址:github.com/netyouli/WH…
本人其他优秀开源项目Github

高性能Sqlite存储模型对象解密相关推荐

  1. android sqlite存对象,【Android基础】Android SQLite存储自定义对象

    Android SQLite存储自定义对象 在SQLite数据库中可存储的数据类型有NULL.INTEGER.REAL(浮点型).TEXT.BOOL,一共是五种数据类型.在Android开发中,我们存 ...

  2. 高性能云原生数据对象存储MinIO实战-上

    文章目录 概述 定义 特性 架构设计 推荐配置 常见应用场景 部署 支持部署方式 部署常见错误 快速入门MinIO Minio纠删码快速入门 分布式MinIO 概述 定义 MinIO 官网地址 htt ...

  3. android sqlite 存储对象,SQLite存储对象

    前言 sqliteDataBase能存储的数据类型有: 1.NULL:空值. 2.INTEGER:带符号的整型,具体取决有存入数字的范围大小. 3.REAL:浮点数字,存储为8-byte IEEE浮点 ...

  4. 块存储,文件存储和对象存储

         首先,我们介绍这两种传统的存储类型.通常来讲,所有磁盘阵列都是基于Block块的模式(DAS),而所有的NAS产品都是文件级存储 1.块存储         以下列出的两种存储方式都是块存储 ...

  5. 【万字干货】OpenMetric与时序数据库存储模型分析

    摘要:解读OpenMetric规范和指标的模型定义基础上,结合当下主流的时序数据库核心存储及处理技术,尝试让用户(架构师.开发者或使用者)结合自身业务场景选择合适的产品,消除技术选型的困惑. 本文分享 ...

  6. 块存储、文件存储、对象存储这三者和分布式文件存储系统的本质区别

    块存储和文件存储是我们比较熟悉的两种主流的存储类型,而对象存储(Object-based Storage)是一种新的网络存储架构,基于对象存储技术的设备就是对象存储设备(Object-based St ...

  7. 五大存储模型关系模型、键值存储、文档存储、列式存储、图形数据

    五大存储模型关系模型.键值存储.文档存储.列式存储.图形数据 时间:2014-06-12 16:15来源:知行网www.zhixing123.cn 编辑:麦田守望者 昨天跟一同事讨论Sybase是不是 ...

  8. 天翼云从业认证(1.2)存储的概念、体系结构、块存储、对象存储、文件存储以及 RAID 磁盘管理技术

    (1)块存储.对象存储.文件存储 存储方式 技术实现 优势 劣势 代表作 块存储 裸盘上划分逻辑卷,逻辑卷格式化成任意文件系统 支持多种文件系统,传输速度快,提供硬件容错机制 无法实现网络共享 FC- ...

  9. iOS NSUserDefaults 简介 NSUserDefaults 存储自定义对象

    摘要 NSUserDefaults适合存储轻量级的本地数据,一些简单的数据(NSString类型的)例如密码,网址等,NSUserDefaults肯定是首选,但是如果我们自定义了一个对象,对象保存的是 ...

最新文章

  1. linux系统编程:自己动手写一个cp命令
  2. Vue—基础概念—路由(vue-router)
  3. linux shell命令行及脚本编程实例详解_超全整理!这些Shell编程必备知识你都掌握了吗?...
  4. drools 执行函数_Drools可执行模型还活着
  5. C++(1)--概况、开发工具、hello word
  6. 招聘ASP.net高级Web开发工程师
  7. BZOJ 2683: 简单题(CDQ 分治)
  8. C++ dlib实现人脸识别
  9. Matlab实现分离变量法求解一维热传导方程的初边值问题
  10. 形而上认识进程间通信
  11. 正三角java_Java中的的画正三角方法
  12. 【Day4.4】堵车去暹罗商圈吃午餐
  13. 单壁碳纳米管-DNA复合物(SWCNT-DNA)|作用机理
  14. HDU 6438 Buy and Resell (优先队列 or 贪心)
  15. Java在手机平台上的Porting
  16. 再谈桌面虚拟化环境中的默认配置文件与输入法
  17. 计算机页面错误代码,电脑Windows常见错误代码解析
  18. 计算机三级网络技术知识点
  19. 极狐公司官方澄清声明
  20. Echart佛山五区地图-动态地图-江西地图

热门文章

  1. 让浏览器不记住表单元素输入过的内容
  2. jQuery实现拖动布局并将排序结果保存到数据库
  3. 深入了解一下PYTHON中关于SOCKETSERVER的模块-C
  4. 安装SQL Server 2012示例数据库
  5. 优化SQl语句的十个重要步骤
  6. 20175317 《Java程序设计》第一周学习总结
  7. 转:经典论文翻译导读之《Google File System》
  8. Python爬虫之urllib模块2
  9. 我为什么选择使用容器?
  10. ios辅助功能之voiceover实战