【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", @, @175l, @, @, [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 = ; 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:]; 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 > ) {
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[];
} else {
result = ;
}
}];
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 = ; 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 = ; 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:]; 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", @, @175l, @, @, [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
【iOS】FMDB封装,查询自动mapping的更多相关文章
- IOS FMDB模糊查询
http://blog.sina.com.cn/s/blog_9630f1310101fx1d.html /查询记录 -(NSArray*)selectitemDream_desc:(JiemengS ...
- IOS开发-封装数据库sqlite3之为何选择FMDB
为什么使用第三方轻量级框架FMDB? FMDB是用于进行数据存储的第三方的框架,它与SQLite与Core Data相比较,存在很多优势. FMDB是面向对象的,它以OC的方式封装了SQLite的C语 ...
- iOS FMDB的使用(增,删,改,查,sqlite存取图片)
iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...
- CI中写原生SQL(封装查询)
封装查询 封装,通过让系统为你组装各个查询语句,能够简化你的查询语法.参加下面的范例: $sql = "SELECT * FROM some_table WHERE id = ? AND s ...
- iOS 微信 音频 视频自动播放
iOS 微信 音频 视频自动播放 http://www.w3ctech.com/topic/1165
- hibernate封装查询,筛选条件然后查询
// 封装查询条件 @Test public void transmitParameter() { Map map = new HashMap<String, String>(); // ...
- 在iOS微信浏览器中自动播放HTML5 audio(音乐)的2种正确方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- ios设置音乐audio自动播放
因为audio标签的自动播放:autoplay.在ios系统中不能自动播放,此时需要设置,在进入页面自动播放音乐. 第一步,先引入js微信 <script src="js/jweixi ...
- python3操作mysql数据库表01(封装查询单条、多条数据)
#!/usr/bin/env python# -*- coding:UTF-8 -*- import pymysql# import os'''封装查询单条.多条数据'''# os.environ[' ...
随机推荐
- Navi.Soft30.框架.WinForm.开发手册
阅读导航 Navi.Soft30.Core类库.开发手册 http://www.cnblogs.com/xiyang1011/p/5709489.html Navi.Soft30.框架.WinForm ...
- Microsoft.Web.RedisSessionStateProvider 运行异常问题
System.TimeoutException: Timeout performing GET MyKey, inst: 2, mgr: Inactive, queue: 6, qu: 0, qs: ...
- appToken 来源
.appToken的来源ActivityRecord, 用于在ActivityManagerService中记录Activity相关的信息(ActivityThread端 用的是ActivityCli ...
- Mac mongodb 配置安装
简单总结就几条,比较简单配置mongodb. 1,首先下载安装包:百度云下载地址 2,下载之后解压到自己常放的工作目录下,然后开始配置一下你的Mac环境 vim ~/.bash_profile 添加m ...
- alt属性和title属性差异---终于分清楚了!
凡是接触过前端的开发者,相信都会接触到<img>标签,自然alt title更是不会陌生,但对他们真正的含义和使用方法,你确定了解吗? 参考: http://www.junchenwu.c ...
- javap生成的字节码
https://www.zhihu.com/question/49470442/answer/135812845http://blog.csdn.net/tzs_1041218129
- MTNET 自用ios网络库开源
短短两天就在https://git.oschina.net/gangwang/MTNET这里收获15个星 github 5星, 值得收藏! MTNET 自用ios网络库开源, 自用很久了,在数歀上架的 ...
- spark MySQL jar 包
/** * Created by songcl on 2016/6/24. */ import java.sql.DriverManager //val sqlContext = new org.ap ...
- java攻城狮之路(Android篇)--MP3 MP4、拍照、国际化、样式主题、图片移动和缩放
一.MP3播放器 查看Android API文档可以看到MediaPlayer状态转换图: 练习: package com.shellway.mp3player; import java.io.Fil ...
- 在线头像上传(js)
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...