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. 手动安装 atom 扩展包 packages

    由于某些原因, 我们下载 atom 扩展时发现速度特别慢, 或者根本无法下载, 那我们可以尝试手动安装 首先, 从 github 上下载(或其它地方) 扩展包, 解压 进入该文件夹, 找到 packa ...

  2. ORACLE 10g下载地址

    ORACLE 10g下载地址 oracle 下载还需要用户名我自己注册了个方便大家使用下载 密码是这个 一般不会动了 大家也不用帮我找回密码了 每次都改 也很麻烦的如果有需要可以帮我浏览下 右侧的需要 ...

  3. Target runtime com.genuitec.runtime.generic.jee50 is not defined

    导入别人的工程,发现报错Target runtime com.genuitec.runtime.generic.jee50 is not defined   解决方法:1. 找到工程目录的.setti ...

  4. Swift XML解析库 - SwiftyXMLParser

    经过在CocoaPods中筛选以后,发这个这个比较好用,整理出来 如果有需要可以在Pods命令端输入: pod search xml 这样会搜索出很多相关Xml的第三方库 SwiftyXMLParse ...

  5. [leetcode]Excel Sheet Column Number

    26进制 class Solution { public: int titleToNumber(string s) { ; ; i < s.size(); i++) { n = n * + s[ ...

  6. netbeans项目中排除node_modules文件夹

    由于项目中的javascript使用了gulp编译技术,所以在web目录下有js的源码目录,其中又有gulp所使用的nodeJS模块,node_modules下面小文件很多,所以造成netbeans扫 ...

  7. Groovy split竖杆注意

    前几天将09年写的一个Asp程序使用Grails改造重写,在处理手机号码Split的时候,Asp代码: dim phoneArr phoneArr = split(phones,"|&quo ...

  8. WinFrom 登录窗体 密码保存效果

    WinFrom 登录窗体 保存密码效果 开发CS程序的程序员都会遇到 今天突然想把这个功能加到我的项目中 之后总结下 不多说 上图   如果关闭程序 下次在登录的时候 用户名.密码会自动保留下来  一 ...

  9. IIS+PHP+MYSQL安装配置

    首先下载php-5.2.0-win32.zip,mysql-noinstall-5.0.22-win32.zip和phpMyAdmin-2.9.1.1-all-languages.zip.这三个文件的 ...

  10. java 实现验证码

    package edu.zzuli.common; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; i ...