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的更多相关文章

  1. IOS FMDB模糊查询

    http://blog.sina.com.cn/s/blog_9630f1310101fx1d.html /查询记录 -(NSArray*)selectitemDream_desc:(JiemengS ...

  2. IOS开发-封装数据库sqlite3之为何选择FMDB

    为什么使用第三方轻量级框架FMDB? FMDB是用于进行数据存储的第三方的框架,它与SQLite与Core Data相比较,存在很多优势. FMDB是面向对象的,它以OC的方式封装了SQLite的C语 ...

  3. iOS FMDB的使用(增,删,改,查,sqlite存取图片)

    iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...

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

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

  5. iOS 微信 音频 视频自动播放

    iOS 微信 音频 视频自动播放 http://www.w3ctech.com/topic/1165

  6. hibernate封装查询,筛选条件然后查询

    // 封装查询条件 @Test public void transmitParameter() { Map map = new HashMap<String, String>(); // ...

  7. 在iOS微信浏览器中自动播放HTML5 audio(音乐)的2种正确方式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

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

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

  9. python3操作mysql数据库表01(封装查询单条、多条数据)

    #!/usr/bin/env python# -*- coding:UTF-8 -*- import pymysql# import os'''封装查询单条.多条数据'''# os.environ[' ...

随机推荐

  1. python排序算法的实现-快速排序

    1. 算法描述: 1.先从数列中取出一个数作为基准数. 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边. 3.再对左右区间重复第二步,直到各区间只有一个数. 2.pyth ...

  2. 删除数据报ORA-00600: internal error code, arguments: [ktbesc_plugged]

    Oracle在删除数据是以下错误: ORA-00600: internal error code, arguments: [ktbesc_plugged], [], [], [], [], [], [ ...

  3. java单例之enum实现方式

    传统的两私有一公开(私有构造方法.私有静态实例(懒实例化/直接实例化).公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例), 目前最为安全的实现单例的方法是通过内部静态en ...

  4. MVC ASP.net流程 源代码分析

    AppDomainFactory.cs 1. public Object Create(String appId, String appPath) public Object Create(Strin ...

  5. 解决ubuntu解压zip文件名乱码的问题

    1. 安装7-zip 和 convmv : 命令: sudo apt-get install convmv p7zip-full 2. 解压zip文件: 命令:LANG=C 7z e yourZIPf ...

  6. Object-C Categories和Protocols

    Category 要扩展一个不可修改的类,通常的做法是为该类创建一个子类,在子类中实现想要实现的方法,在Object-C中,可以通过category来实现,并且实现方式更为简单. 现在有如下定义:一个 ...

  7. SSAS:概念梳理

    Dimension Objects 原文 A simple Dimension object is composed of basic information, attributes, and hie ...

  8. Normalize.css

    根据之前的一些项目,总结了一下重置CSS: @charset "UTF-8"; html { background: #FFF; font-size: 62.5%; -ms-tex ...

  9. CSS选择器、优先级与匹配原理

    为了分析Bootstrap源码,所以的先把CSS选择器相关的东东给巩固好 废话就不多说了 CSS 2.1 selectors, Part 1 计算指定选择器的优先级:重新认识CSS的权重 标签的权值为 ...

  10. 关于鼠标事件的screenY,pageY,clientY,layerY,offsetY属性 (详细图解)

    screenY 鼠标相对于显示器屏幕左上角的偏移 pageY 鼠标相对于页面左上角的偏移 (其值不会受滚动条的影响) IE9之下并不支持这个属性 但是可以写点代码计算出来. jQuery中的实现: / ...