【iOS】FMDB封装,查询自动mapping
sqlite几乎所有的App都会用到,但是系统自带的sqlite API是用C语言写的,非常不友好,用起来非常不便,通常我们使用第三方封装好的工具,例如:FMDB(https://github.com/ccgus/fmdb)
FMDB的提供了一种更简单,方便的API,并且还提供了线程安全的队列FMDatabaseQueue用于数据库的读写,关于FMDB的使用,参见github上的描述
在使用FMDB查询表的时候的时候我们一般用下面方式
1、定义一个数据模型PersonModel
@interface PersonModel : NSObject@property (nonatomic, assign) NSInteger peopleId; @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *gender; @property (nonatomic, assign) float weight; @property (nonatomic, assign) double height; @property (nonatomic, assign) short age; @property (nonatomic, assign) long score; @property (nonatomic, strong) NSDate *createTime; @property (nonatomic, assign) BOOL married; @property (nonatomic, strong) NSData *desc;@end
2、插入数据
NSString *sql = @"insert into People(name, gender, weight, height, age, score, createTime, married, desc) values(?,?,?,?,?,?,?,?,?)";NSString *text = @"dataValue";NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];NSArray *param = @[@"bomo", @"male", @70, @175l, @22, @123, [NSDate date], @NO, data];[_queue inDatabase:^(FMDatabase *db) {[db executeUpdate:sql withArgumentsInArray:param];}];
3、查询
NSString *sql = @"select * from People";__block NSMutableArray *people = [NSMutableArray array];[_queue inDatabase:^(FMDatabase *db) {FMResultSet *rs = [db executeQuery:sql];while ([rs next]) { person.name = [rs stringForColumn:@"name"];person.gender = [rs stringForColumn:@"gender"];person.height = [rs doubleForColumn:@"height"];person.score = [rs longForColumn:@"score"];person.createTime = [rs dateForColumn:@"createTime"];person.married = [rs boolForColumn:@"married"];person.desc = [rs dataForColumn:@"desc"];//下面几种方式读取数据会导致数据类型不一致的问题//person.peopleId = [rs intForColumn:@"peopleId"]; //person.age = [rs intForColumn:@"age"];//person.weight = [rs doubleForColumn:@"weight"]; [people addObject:person];}[rs close];}];
这里的查询方法需要对每一个属性进行读取和赋值,并且可能有数据类型不一致的问题,比如 读取出来的int 赋值给NSInteger 类型,double类型赋值给float类型,下面我们对查询方法进行改造,让其变得更通用,可以自动映射查询结果到Model,并提供数据库列名到属性名之间的映射
1、定义映射协议
#import <Foundation/Foundation.h>//实现数据库列名到model属性名的映射 @protocol ColumnPropertyMappingDelegate <NSObject>@required - (NSDictionary *)columnPropertyMapping;@end
2、修改PersonModel模型
#import <Foundation/Foundation.h> #import "ColumnPropertyMappingDelegate.h"@interface PersonModel : NSObject <ColumnPropertyMappingDelegate>@property (nonatomic, assign) NSInteger peopleId; @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *gender; @property (nonatomic, assign) float weight; @property (nonatomic, assign) double height; @property (nonatomic, assign) short age; @property (nonatomic, assign) long score; @property (nonatomic, strong) NSDate *createTime; @property (nonatomic, assign) BOOL married; @property (nonatomic, strong) NSData *desc;@end@implementation PersonModel- (NSDictionary *)columnPropertyMapping {return @{@"id": @"peopleId",@"str1": @"name",@"str2": @"gender",@"float1": @"weight",@"double1": @"height",@"short1": @"age",@"long1": @"score",@"date1": @"createTime",@"bool1": @"married",@"data1": @"desc"}; }@end
数据库的列名如果与Model的属性名不一致,可以通过改映射函数进行配置
3、查询函数,关键方法
/*** 执行查询操作,自定构造models集合** @param sql sql语句* @param args sql参数* @param modelClass 结果集model类型* @param block 对model执行自定义操作** @return 查询结果集*/ - (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)args modelClass:(Class)modelClass performBlock:(void (^)(id model, FMResultSet *rs))block {__block NSMutableArray *models = [NSMutableArray array];[_queue inDatabase:^(FMDatabase *db) {NSDictionary *mapping = nil;FMResultSet *rs = [db executeQuery:sql withArgumentsInArray:args];while ([rs next]) {id model = [[modelClass alloc] init];if(!mapping && [model conformsToProtocol:@protocol(ColumnPropertyMappingDelegate)]) {//实现了列-属性转换协议mapping = [model columnPropertyMapping];}for (int i = 0; i < [rs columnCount]; i++) {//列名NSString *columnName = [rs columnNameForIndex:i];//进行数据库列名到model之间的映射转换,拿到属性名NSString *propertyName;if(mapping) {propertyName = mapping[columnName];if (propertyName == nil) {//如果映射未定义,则视为相同propertyName = columnName;}} else {propertyName = columnName;}objc_property_t objProperty = class_getProperty(modelClass, propertyName.UTF8String);//如果属性不存在,则不操作if (objProperty) {if(![rs columnIndexIsNull:i]) {[self setProperty:model value:rs columnName:columnName propertyName:propertyName property:objProperty];}}NSAssert(![propertyName isEqualToString:@"description"], @"description为自带方法,不能对description进行赋值,请使用其他属性名或请ColumnPropertyMappingDelegate进行映射");}//执行自定义操作if (block) {block(model, rs);}[models addObject:model];}[rs close];}];return models; }/*** 进行属性赋值*/ - (void)setProperty:(id)model value:(FMResultSet *)rs columnName:(NSString *)columnName propertyName:(NSString *)propertyName property:(objc_property_t)property {// @"f":@"float",// @"i":@"int",// @"d":@"double",// @"l":@"long",// @"c":@"BOOL",// @"s":@"short",// @"q":@"long",// @"I":@"NSInteger",// @"Q":@"NSUInteger",// @"B":@"BOOL", NSString *firstType = [[[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject] substringFromIndex:1];if ([firstType isEqualToString:@"f"]) {NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.floatValue) forKey:propertyName];} else if([firstType isEqualToString:@"i"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.intValue) forKey:propertyName];} else if([firstType isEqualToString:@"d"]){[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} else if([firstType isEqualToString:@"l"] || [firstType isEqualToString:@"q"]){[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} else if([firstType isEqualToString:@"c"] || [firstType isEqualToString:@"B"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.boolValue) forKey:propertyName];} else if([firstType isEqualToString:@"s"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.shortValue) forKey:propertyName];} else if([firstType isEqualToString:@"I"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.integerValue) forKey:propertyName];} else if([firstType isEqualToString:@"Q"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.unsignedIntegerValue) forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSData\""]){NSData *value = [rs dataForColumn:columnName];[model setValue:value forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSDate\""]){NSDate *value = [rs dateForColumn:columnName];[model setValue:value forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSString\""]){NSString *value = [rs stringForColumn:columnName];[model setValue:value forKey:propertyName];} else {[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} }
我们把数据库的查询和更新方法封装成DbService
#import <Foundation/Foundation.h> @class FMResultSet;@interface DbService : NSObject- (instancetype)initWithPath:(NSString *)path;/*** 查询第一行第一列的数据*/ - (id)executeScalar:(NSString *)sql param:(NSArray *)param;/*** 查询行数*/ - (NSInteger)rowCount:(NSString *)tableName;/*** 更新数据*/ - (BOOL)executeUpdate:(NSString *)sql param:(NSArray *)param;#pragma mark - 查询操作自动构建Model/*** 执行查询操作,自定构造models集合** @param sql sql语句* @param args sql参数* @param modelClass 结果集model类型** @return 查询结果集*/ - (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)args modelClass:(Class)modelClass;/*** 执行查询操作,自定构造models集合** @param sql sql语句* @param args sql参数* @param modelClass 结果集model类型* @param block 对model执行自定义操作** @return 查询结果集*/- (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)args modelClass:(Class)modelClass performBlock:(void (^)(id model, FMResultSet *rs))block;/*** 查询结果集取得model集合** @param rs 数据库查询结果集* @param modelClass 结果集model类型** @return 查询结果集*/ - (NSArray *)resultForModels:(FMResultSet *)rs modelClass:(Class)modelClass;/*** 查询结果集取得model集合** @param rs 数据库查询结果集* @param modelClass 结果集model类型* @param block 对model执行自定义操作** @return 查询结果集*/ - (NSArray *)resultForModels:(FMResultSet *)rs modelClass:(Class)modelClass performBlock:(void (^)(id model, FMResultSet *rs))block;@end
![](/assets/blank.gif)
![](/assets/blank.gif)
#import "DbService.h" #import <objc/runtime.h> #import "FMDB.h" #import "ColumnPropertyMappingDelegate.h"#import "PersonModel.h"@interface DbService () {FMDatabaseQueue *_queue; }@end@implementation DbService- (instancetype)initWithPath:(NSString *)path {if (self = [super init]) {_queue = [FMDatabaseQueue databaseQueueWithPath:path];}return self; }- (BOOL)executeUpdate:(NSString *)sql param:(NSArray *)param {__block BOOL result = NO;[_queue inDatabase:^(FMDatabase *db) {if (param && param.count > 0) {result = [db executeUpdate:sql withArgumentsInArray:param];} else {result = [db executeUpdate:sql];}}];return result;}- (id)executeScalar:(NSString *)sql param:(NSArray *)param {__block id result;[_queue inDatabase:^(FMDatabase *db) {FMResultSet *rs = [db executeQuery:sql withArgumentsInArray:param];if ([rs next]) {result = rs[0];} else {result = 0;}}];return result; }- (NSInteger)rowCount:(NSString *)tableName {NSNumber *number = (NSNumber *)[self executeScalar:[NSString stringWithFormat:@"SELECT COUNT(*) FROM %@", tableName] param:nil];return [number longValue]; }#pragma mark - #pragma mark -- 自动创建model查询方法 - (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)args modelClass:(Class)modelClass {return [self executeQuery:sql withArgumentsInArray:args modelClass:modelClass performBlock:nil]; }- (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)args modelClass:(Class)modelClass performBlock:(void (^)(id model, FMResultSet *rs))block {__block NSMutableArray *models = [NSMutableArray array];[_queue inDatabase:^(FMDatabase *db) {NSDictionary *mapping = nil;FMResultSet *rs = [db executeQuery:sql withArgumentsInArray:args];while ([rs next]) {id model = [[modelClass alloc] init];if(!mapping && [model conformsToProtocol:@protocol(ColumnPropertyMappingDelegate)]) {//实现了列-属性转换协议mapping = [model columnPropertyMapping];}for (int i = 0; i < [rs columnCount]; i++) {//列名NSString *columnName = [rs columnNameForIndex:i];//进行数据库列名到model之间的映射转换,拿到属性名NSString *propertyName;if(mapping) {propertyName = mapping[columnName];if (propertyName == nil) {//如果映射未定义,则视为相同propertyName = columnName;}} else {propertyName = columnName;}objc_property_t objProperty = class_getProperty(modelClass, propertyName.UTF8String);//如果属性不存在,则不操作if (objProperty) {if(![rs columnIndexIsNull:i]) {[self setProperty:model value:rs columnName:columnName propertyName:propertyName property:objProperty];}}NSAssert(![propertyName isEqualToString:@"description"], @"description为自带方法,不能对description进行赋值,请使用其他属性名或请ColumnPropertyMappingDelegate进行映射");}//执行自定义操作if (block) {block(model, rs);}[models addObject:model];}[rs close];}];return models; }/*** 解析结果集(models)*/ - (NSArray *)resultForModels:(FMResultSet *)rs modelClass:(Class)modelClass {return [self resultForModels:rs modelClass:modelClass performBlock:nil]; }- (NSArray *)resultForModels:(FMResultSet *)rs modelClass:(Class)modelClass performBlock:(void (^)(id model, FMResultSet *rs))block; {NSDictionary *mapping = nil;NSMutableArray *models = [NSMutableArray array];while ([rs next]) {id model = [[modelClass alloc] init];if(!mapping && [model conformsToProtocol:@protocol(ColumnPropertyMappingDelegate)]) {//实现了列-属性转换协议mapping = [model columnPropertyMapping];}for (int i = 0; i < [rs columnCount]; i++) {//列名NSString *columnName = [rs columnNameForIndex:i];//进行数据库列名到model之间的映射转换,拿到属性名NSString *propertyName;if(mapping) {propertyName = mapping[columnName];if (propertyName == nil) {propertyName = columnName;}} else {propertyName = columnName;}objc_property_t objProperty = class_getProperty(modelClass, propertyName.UTF8String);//如果属性不存在,则不操作if (objProperty) {if(![rs columnIndexIsNull:i]) {[self setProperty:model value:rs columnName:columnName propertyName:propertyName property:objProperty];}}NSAssert(![propertyName isEqualToString:@"description"], @"description为自带方法,不能对description进行赋值,请使用其他属性名或请ColumnPropertyMappingDelegate进行映射");}//执行自定义操作if (block) {block(model, rs);}[models addObject:model];}[rs close];return models; }/*** 进行属性赋值*/ - (void)setProperty:(id)model value:(FMResultSet *)rs columnName:(NSString *)columnName propertyName:(NSString *)propertyName property:(objc_property_t)property {// @"f":@"float",// @"i":@"int",// @"d":@"double",// @"l":@"long",// @"c":@"BOOL",// @"s":@"short",// @"q":@"long",// @"I":@"NSInteger",// @"Q":@"NSUInteger",// @"B":@"BOOL", NSString *firstType = [[[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject] substringFromIndex:1];if ([firstType isEqualToString:@"f"]) {NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.floatValue) forKey:propertyName];} else if([firstType isEqualToString:@"i"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.intValue) forKey:propertyName];} else if([firstType isEqualToString:@"d"]){[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} else if([firstType isEqualToString:@"l"] || [firstType isEqualToString:@"q"]){[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} else if([firstType isEqualToString:@"c"] || [firstType isEqualToString:@"B"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.boolValue) forKey:propertyName];} else if([firstType isEqualToString:@"s"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.shortValue) forKey:propertyName];} else if([firstType isEqualToString:@"I"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.integerValue) forKey:propertyName];} else if([firstType isEqualToString:@"Q"]){NSNumber *number = [rs objectForColumnName:columnName];[model setValue:@(number.unsignedIntegerValue) forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSData\""]){NSData *value = [rs dataForColumn:columnName];[model setValue:value forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSDate\""]){NSDate *value = [rs dateForColumn:columnName];[model setValue:value forKey:propertyName];} else if([firstType isEqualToString:@"@\"NSString\""]){NSString *value = [rs stringForColumn:columnName];[model setValue:value forKey:propertyName];} else {[model setValue:[rs objectForColumnName:columnName] forKey:propertyName];} }@end
PersonModel.m
创建一个PeopleService演示一下
![](/assets/blank.gif)
![](/assets/blank.gif)
#import <Foundation/Foundation.h>@interface PeopleService : NSObject+ (instancetype)shareInstance;- (void)createTable;- (void)insertOnePerson;- (NSArray *)query;@end
PeopleService.h
![](/assets/blank.gif)
![](/assets/blank.gif)
#import "PersonModel.h" #import "PeopleService.h" #import "DbService.h"@interface PeopleService () {DbService *_dbService; }@end@implementation PeopleService+ (instancetype)shareInstance {static id instance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance = [[self alloc] init];});return instance; }- (instancetype)init {if (self = [super init]) {NSString *dbName = @"people.db";NSString *directory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];NSString *dbPath = [directory stringByAppendingPathComponent:dbName];_dbService = [[DbService alloc] initWithPath:dbPath];}return self; }- (void)createTable {NSString *sql = @"CREATE TABLE People ( \id INTEGER PRIMARY KEY AUTOINCREMENT, \str1 TEXT, \str2 TEXT, \float1 REAL, \double1 INTEGER, \short1 REAL, \long1 REAL, \date1 TEXT, \bool1 INTEGER, \data1 BLOB \)";[_dbService executeUpdate:sql param:nil]; }- (void)insertOnePerson {NSString *sql = @"insert into People(str1, str2, float1, double1, short1, long1, date1, bool1, data1) values(?,?,?,?,?,?,?,?,?)";NSString *text = @"dataValue";NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];NSArray *param = @[@"bomo", @"male", @70, @175l, @22, @123, [NSDate date], @NO, data];[_dbService executeUpdate:sql param:param]; }- (NSArray *)query {return [_dbService executeQuery:@"select * from People" withArgumentsInArray:nil modelClass:[PersonModel class]]; }@end
PeopleService.m
测试
[[PeopleService shareInstance] createTable];[[PeopleService shareInstance] insertOnePerson];NSArray *people = [[PeopleService shareInstance] query];for (PersonModel *person in people) {NSLog(@"name = %@, age = %d", person.name, person.age);}
我们看一下数据库的表结构
查询操作只需要一行代码,其他的都交给DbService,减少代码量,减少人为错误,由于个人水平有限,如有疏漏或问题,欢迎回复,如果大家有更好的方式,可以一起讨论
Demo:http://files.cnblogs.com/files/bomo/FmdbDemo.zip
转载于:https://www.cnblogs.com/bomo/p/4665376.html
【iOS】FMDB封装,查询自动mapping相关推荐
- ios mysql数据库查询语句_ios fmdb数据库查询语句
iOS开发数据库篇-FMDB简单介绍 iOS开发数据库篇-FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语 ...
- ios FMDB数据库添删改查应用
本文原创,转载请注明出处! 哦吼吼,又研究了几天,把FMDB这个封装好的数据库搞定了,写了个简单的例子,基于FMDB的添删改查操作,界面很一般了,代码可能比较乱,希望不要伤了各位的眼睛.依旧是纯代码实 ...
- CI中写原生SQL(封装查询)
封装查询 封装,通过让系统为你组装各个查询语句,能够简化你的查询语法.参加下面的范例: $sql = "SELECT * FROM some_table WHERE id = ? AND s ...
- iOS 微信 音视频自动播放 原生接口WeixinJSBridge API(一些整理 小技巧)
原文链接1:https://www.w3ctech.com/topic/1165 原文链接2:https://www.cnblogs.com/jasonduan/p/5635048.html 做一下整 ...
- 12C 新特性 | 标量子查询自动转换
有超过6年超大型数据库专业服务经验,擅长数据库解决方案设计与项目管理:在多年的技术实践中,先后为运营商(移动.电信).银行.保险.制造业等各行业客户的业务关键型系统提供了运维.升级.性能优化.项目实施 ...
- ios 短信验证码自动填充时总是被复制两遍
ios 短信验证码自动填充时总是被复制两遍 解决: 限制input标签的maxlength:
- ios设置音乐audio自动播放
因为audio标签的自动播放:autoplay.在ios系统中不能自动播放,此时需要设置,在进入页面自动播放音乐. 第一步,先引入js微信 <script src="js/jweixi ...
- 7-5 查询自动售货机中商品的价格
7-5 查询自动售货机中商品的价格 给定四种商品,分别是薯片(crisps).爆米花(popcorn).巧克力(chocolate)和可乐(cola),单价分别对应为3.0元/公斤.2.5元/公斤.4 ...
- 超全!元器件封装查询图表(收藏)
来源:满天芯,整理:晓宇 微信公众号:芯片之家(ID:chiphome-dy) 给大家分享一份干货文档,元器件封装查询图表,文档整理了的元器件封装比较齐全,几乎都有对应的封装图,可以很方便的查询封装形 ...
最新文章
- keras 的 example 文件 mnist_acgan.py 解析
- 计算机自动化技术要学什么,【经验分享】PLC学习的5个阶段,自动化工程师看看你属于哪个阶段?...
- java 分页_Spring Boot + MyBatis 如何借助PageHelper插件实现分页效果
- vue 导入excel插件_Vue框架下实现导入导出Excel、导出PDF
- 一切事物皆对象_基础篇
- mysql 日期详解_在MySQL中解析日期
- 边界布局BorderLayout源码解析
- SQL必知必会数据库数据
- java 坦克大战 教程_马士兵老师/坦克大战/java基础/网络编程 (9.1G)视频教程下载...
- USB Server应用于税控盘的远程集中管理
- Android能装到电脑上吗,怎么在电脑上装安卓系统
- 【戏言、昔言、惜言】谭惜言写了一辈子的戏,真情假意,全在戏言里。
- 2019年第十届蓝桥杯A组国赛(C/C++)
- 3D建模和3D渲染吃什么硬件?专业图形显卡和游戏显卡区别
- BTT6030芯片手册
- 我国研发出勒索病毒防御软件:能阻止其破坏文件
- 极域课堂分发文件与一键开关机教程
- win10将HTML动态做桌面壁纸,Win10 怎么制作动态界面壁纸
- 网管必知:常用电脑密码破解
- 回路电感详细介绍(环路电感)
热门文章
- Gartner合伙人对“新基建”五大关键领域的见解及建议
- Redis进阶 - 缓存问题:一致性, 穿击, 穿透, 雪崩, 污染等
- keil在线仿真STM32,需要点三次全速运行才能跑起来
- Odoo性能优化实战
- 南邮23考研复试上机[1006最大公约数和最小公倍数]
- Markdown系列(6)- 如何优雅地在Markdown中输入数学公式
- Android——Matirx 自动生成火焰图小工具
- 使用MindStudio进行ICNet模型推理与训练
- Vue移动端项目中px转rem的两种方法
- 整理有关智慧城市相关的建设方案、国家标准、国家规范、解决方案和参考资料