FMDB处理动态插入语句
昨天做一个需求,参数的数量不确定,所以无法使用这个API:
- (BOOL)executeUpdate:(NSString*)sql, ...
但是用
- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments
可以很方便地处理这个场景,本文总结一下
两类insert语句
因为不管使用哪种API,都需要先拼接sql语句,所以先了解一下sql insert语句的写法。有2种,比较常见的是指定列名的insert:
insert into table_name (c1,c2,c3) values (v1,v2,v3);
如果自己指定了目标列名,那么插入的列的数量可以少于table schema,列的顺序也可以任意指定。第2种是不指定列名的insert:
insert into table_name values (v1,v2,v3);
这种写法不需要自己指定列名,比较方便,但是values里需要包含所有的列,而且顺序要和table schema一致
FMDB的2种常用API
我自己比较常用的FMDB API有2个。如果参数的数量是确定的,而且不多,可以用这个API:
NSString *sql = @"insert into users values(:id, :name, :age)";
[db executeUpdate:sql, id, name, age];上面的代码用了:key的语法,或者用?,效果也是一样的
NSString *sql = @"insert into users values(?,?,?)";
[db executeUpdate:sql, id, name, age];另外一种场景,是参数的数量不确定,或者参数数量非常多,那上面这个API就不太方便,FMDB提供了另一个API,允许传入一个NSDictionary作为参数:
NSString *sql = @"insert into users values(:id, :name, :age)";
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"id123", @"id", @"kyfxbl", @"name", , @"age", nil];
[db executeUpdate:sql withParameterDictionary:dict];注意Dictionary里的key需要和sql里的:key一致,FMDB根据key值来决定如何绑定
遇到的问题
我昨天就是用上面的第二种API来实现,不过遇到一个错误,总是报table_name.id may not be NULL。最后发现原因,是拼的sql采用了不指定列名的方式,类似于:
NSString *sql = @"insert into table_name values (:k1,:k2,:k3)";
然后刚好NSDictionary里元素的顺序又是错的,第一个元素的值是null,造成id为空,插入失败。其实关键不在于FMDB,而是sql没拼对。主要是我不了解FMDB的这个API,我以为它会自己处理顺序,实际上,FMDB不处理SQL拼接,只处理参数变量替换
有2个办法可以解决这个问题,一个办法是调整Dictionary里元素的顺序,使它和table schema列定义的顺序一致,不过比较困难。另一个办法就是重新拼sql,指定好列名
实际的代码
下面是实现的代码,删除了无关代码,以免干扰
-(void) handleJsonObject:(NSDictionary*) tableName
{
NSString *sql = [self assembleInsertSql:tableName];// 拼装sql语句:insert into table_name (c1,c2,c3) values (:a,:b,:c); if(!sql){
return;// 说明data里无数据,不需要操作数据库
} NSString *dbFilePath = [YLSGlobalUtils getDatabaseFilePath];
FMDatabase *db = [FMDatabase databaseWithPath:dbFilePath];
[db open]; NSArray *datas = [tableName objectForKey:@"data"];
for(NSDictionary *data in datas){
NSMutableDictionary *mutable = [NSMutableDictionary dictionaryWithDictionary:data];
[mutable removeObjectForKey:@"_id"];
BOOL result = [db executeUpdate:sql withParameterDictionary:mutable];
if(!result){
NSLog([NSString stringWithFormat:@"%d", [db lastErrorCode]], nil);
NSLog([db lastErrorMessage], nil);
}
} [db close];
} // 返回格式:insert into table_name (c1,c2,c3) values (:a,:b,:c);
-(NSString*) assembleInsertSql:(NSDictionary*) tableName
{
NSArray *datas = [tableName objectForKey:@"data"]; if([datas count] == ){
return nil;
} NSDictionary *record = [datas objectAtIndex:];// 取出第一条记录,为了拿到列名
NSArray *columns = [record allKeys]; NSString *table = [tableName objectForKey:@"tableName"];
NSString *prefix = [NSString stringWithFormat:@"insert into %@ (", table]; NSMutableString *middle = [NSMutableString new];
for(int i=;i<[columns count];i++){
NSString *columnName = [columns objectAtIndex:i];// 列名
if(![@"_id" isEqualToString:columnName]){
[middle appendString:columnName];
[middle appendString:@","];
}
}
NSString* cuttedMiddle = [YLSGlobalUtils removeLastOneChar:middle]; NSMutableString *suffix = [NSMutableString new];
[suffix appendString:@") values ("];
for(int i=;i<[columns count];i++){
NSString *columnName = [columns objectAtIndex:i];// 列名
if(![@"_id" isEqualToString:columnName]){
[suffix appendString:@":"];
[suffix appendString:columnName];
[suffix appendString:@","];
}
}
NSString *cuttedSuffix = [YLSGlobalUtils removeLastOneChar:suffix]; NSMutableString *sql = [NSMutableString new];
[sql appendString:prefix];
[sql appendString:cuttedMiddle];
[sql appendString:cuttedSuffix];
[sql appendString:@");"];
return sql;
}通过上面拼接sql的方法,虽然NSDictionary里的元素顺序不确定,但是列名和values里的顺序总是保证一致的,不会影响最后的插入
延伸阅读:
FMDB处理动态插入语句的更多相关文章
- MyBatis的动态插入语句(经常报‘无效的列类型’)
最近在工作中经常遇到一个情况:通过mybatis的标签执行插入语句,当表中字段比较多的时候,需要全部插入,而有时候的需求是只插入其中几个字段,但是会报错. 原来的语句,必须把所有字段都Set值. &l ...
- 动态sql语句基本语法--Exec与Exec sp_executesql 的区别
http://www.cnblogs.com/goody9807/archive/2010/10/19/1855697.html 动态sql语句基本语法 1 :普通SQL语句可以用Exec执行 ...
- MyBatis学习(三)、动态SQL语句
三.动态SQL语句 有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的,也许我们需要参数为空时,是查出全部的信息.使用Orac ...
- 三、动态SQL语句
//备注:该博客引自:http://limingnihao.iteye.com/blog/106076 有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空, ...
- 存储过程中执行动态Sql语句
MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就 ...
- MyBatis学习总结_11_MyBatis动态Sql语句
MyBatis中对数据库的操作,有时要带一些条件,因此动态SQL语句非常有必要,下面就主要来讲讲几个常用的动态SQL语句的语法 MyBatis中用于实现动态SQL的元素主要有: if choose(w ...
- 【转】Hibernate利用@DynamicInsert和@DynamicUpdate生成动态SQL语句
原文链接:http://www.cnblogs.com/quanyongan/p/3152290.html 最近在使用Hibernate4中,发现两个很有奥秘的注解 @DynamicInsert 和 ...
- Oracle基础 动态SQL语句
一.静态SQL和动态SQL的概念. 1.静态SQL 静态SQL是我们常用的使用SQL语句的方式,就是编写PL/SQL时,SQL语句已经编写好了.因为静态SQL是在编写程序时就确定了,我们只能使用SQL ...
- MyBatis学习 之 三、动态SQL语句
目录(?)[-] 三动态SQL语句 selectKey 标签 if标签 if where 的条件判断 if set 的更新语句 if trim代替whereset标签 trim代替set choose ...
随机推荐
- Android 用Fragment创建一个选项卡
本文结合之前的动态创建fragment来进行一个实践,来实现用Fragment创建一个选项卡 本文地址:http://www.cnblogs.com/wuyudong/p/5898075.html,转 ...
- Android 获取系统的联系人
本文主要介绍android中怎样获取系统的联系人数据 首先打开模拟器 点击联系人图标按钮 说明系统联系人数据库是空的,打开File explorer,找到data/data下面的文件夹: 将conta ...
- (20160602)开源第三方学习之SDWebImage
这个类库提供一个UIImageView类别以支持加载来自网络的远程图片.具有缓存管理.异步下载.同一个URL下载次数控制和优化等特征. 地址:https://github.com/rs/SDWebIm ...
- objective-c系列-NSString
C中没有字符串变量的概念 只有一个字符串常量的概念 即: “abcd” 在c中,用一个字符串指来指向一个内存地址, 然后从该地址往后,遇到'\0'结束,这一段 内存就表述为一个字符串 char * ...
- 用Reveal分析第三方App的UI
文章出自:听云博客 Reveal简介: 这是个神奇的工具,它能常透彻地分析个App的UI结构. 这个工具包括两部分,部分是在PC上运行的一个独立应用,即Reveal.app,另一部分代码在你要分析的某 ...
- 【代码笔记】iOS-看图听声音
一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> #import <AVFoundation/AVFo ...
- UIPickerView简单应用
下面是一些效果图 下面是代码.有些枯燥 , 其实并不难 . #import <UIKit/UIKit.h> @interface ViewController : UIViewContro ...
- android中基于HTML模板的方式嵌入SWF
继上一篇 利用webview实现在andorid中嵌入swf 这篇继续说说通过html模板的方式来嵌入SWF,这样做的好处最直观的就是可以把html,swf和android代码串起来,交互操作很方便( ...
- 关于update set from where
http://blog.csdn.net/xcbsdu/article/details/6736503 下面是这样一个例子: 两个表a.b,想使b中的memo字段值等于a表中对应id的name值 ...
- (1)编写一个接口ShapePara,要求: 接口中的方法: int getArea():获得图形的面积。int getCircumference():获得图形的周长 (2)编写一个圆类Circle,要求:圆类Circle实现接口ShapePara。 该类包含有成员变量: radius:public 修饰的double类型radius,表示圆的半径。 x:private修饰的double型变量x,
package com.hanqi.test; //创建接口 public interface ShapePara { //获取面积的方法 double getArea(); //获取周长的方法 do ...