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

#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演示一下

#import <Foundation/Foundation.h>@interface PeopleService : NSObject+ (instancetype)shareInstance;- (void)createTable;- (void)insertOnePerson;- (NSArray *)query;@end

PeopleService.h

#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相关推荐

  1. ios mysql数据库查询语句_ios fmdb数据库查询语句

    iOS开发数据库篇-FMDB简单介绍 iOS开发数据库篇-FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语 ...

  2. ios FMDB数据库添删改查应用

    本文原创,转载请注明出处! 哦吼吼,又研究了几天,把FMDB这个封装好的数据库搞定了,写了个简单的例子,基于FMDB的添删改查操作,界面很一般了,代码可能比较乱,希望不要伤了各位的眼睛.依旧是纯代码实 ...

  3. CI中写原生SQL(封装查询)

    封装查询 封装,通过让系统为你组装各个查询语句,能够简化你的查询语法.参加下面的范例: $sql = "SELECT * FROM some_table WHERE id = ? AND s ...

  4. iOS 微信 音视频自动播放 原生接口WeixinJSBridge API(一些整理 小技巧)

    原文链接1:https://www.w3ctech.com/topic/1165 原文链接2:https://www.cnblogs.com/jasonduan/p/5635048.html 做一下整 ...

  5. 12C 新特性 | 标量子查询自动转换

    有超过6年超大型数据库专业服务经验,擅长数据库解决方案设计与项目管理:在多年的技术实践中,先后为运营商(移动.电信).银行.保险.制造业等各行业客户的业务关键型系统提供了运维.升级.性能优化.项目实施 ...

  6. ios 短信验证码自动填充时总是被复制两遍

    ios 短信验证码自动填充时总是被复制两遍 解决: 限制input标签的maxlength:

  7. ios设置音乐audio自动播放

    因为audio标签的自动播放:autoplay.在ios系统中不能自动播放,此时需要设置,在进入页面自动播放音乐. 第一步,先引入js微信 <script src="js/jweixi ...

  8. 7-5 查询自动售货机中商品的价格

    7-5 查询自动售货机中商品的价格 给定四种商品,分别是薯片(crisps).爆米花(popcorn).巧克力(chocolate)和可乐(cola),单价分别对应为3.0元/公斤.2.5元/公斤.4 ...

  9. 超全!元器件封装查询图表(收藏)

    来源:满天芯,整理:晓宇 微信公众号:芯片之家(ID:chiphome-dy) 给大家分享一份干货文档,元器件封装查询图表,文档整理了的元器件封装比较齐全,几乎都有对应的封装图,可以很方便的查询封装形 ...

最新文章

  1. keras 的 example 文件 mnist_acgan.py 解析
  2. 计算机自动化技术要学什么,【经验分享】PLC学习的5个阶段,自动化工程师看看你属于哪个阶段?...
  3. java 分页_Spring Boot + MyBatis 如何借助PageHelper插件实现分页效果
  4. vue 导入excel插件_Vue框架下实现导入导出Excel、导出PDF
  5. 一切事物皆对象_基础篇
  6. mysql 日期详解_在MySQL中解析日期
  7. 边界布局BorderLayout源码解析
  8. SQL必知必会数据库数据
  9. java 坦克大战 教程_马士兵老师/坦克大战/java基础/网络编程 (9.1G)视频教程下载...
  10. USB Server应用于税控盘的远程集中管理
  11. Android能装到电脑上吗,怎么在电脑上装安卓系统
  12. 【戏言、昔言、惜言】谭惜言写了一辈子的戏,真情假意,全在戏言里。
  13. 2019年第十届蓝桥杯A组国赛(C/C++)
  14. 3D建模和3D渲染吃什么硬件?专业图形显卡和游戏显卡区别
  15. BTT6030芯片手册
  16. 我国研发出勒索病毒防御软件:能阻止其破坏文件
  17. 极域课堂分发文件与一键开关机教程
  18. win10将HTML动态做桌面壁纸,Win10 怎么制作动态界面壁纸
  19. 网管必知:常用电脑密码破解
  20. 回路电感详细介绍(环路电感)

热门文章

  1. Gartner合伙人对“新基建”五大关键领域的见解及建议
  2. Redis进阶 - 缓存问题:一致性, 穿击, 穿透, 雪崩, 污染等
  3. keil在线仿真STM32,需要点三次全速运行才能跑起来
  4. Odoo性能优化实战
  5. 南邮23考研复试上机[1006最大公约数和最小公倍数]
  6. Markdown系列(6)- 如何优雅地在Markdown中输入数学公式
  7. Android——Matirx 自动生成火焰图小工具
  8. 使用MindStudio进行ICNet模型推理与训练
  9. Vue移动端项目中px转rem的两种方法
  10. 整理有关智慧城市相关的建设方案、国家标准、国家规范、解决方案和参考资料