FMDB 数据库使用 executeUpdate

因为这篇文章 搞定一个问题 [__NSCFString bytes]: unrecognized selector sent to instance

iOS-FMDB-数据库使用-(executeUpdate: withArgumentsInArray:,executeUpdate:)

数据库插入NSData

首先把 字典 序列化成 NSData
然后把 NSData 写入数据库
然后读取的时候再反序列化 成字典
但是 读取的时候反序列化失败 崩溃

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString bytes]: unrecognized selector sent to instance 0x7fbc99092000'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010fd94d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010f7f221e objc_exception_throw + 48
    2   CoreFoundation                      0x000000010fe04f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x000000010fd1a005 ___forwarding___ + 1013
    4   CoreFoundation                      0x000000010fd19b88 _CF_forwarding_prep_0 + 120
    5   Foundation                          0x000000010f2bfc2e -[NSKeyedUnarchiver initForReadingWithData:] + 314
    6   Foundation                          0x000000010f2bfa4a +[NSKeyedUnarchiver unarchiveObjectWithData:] + 61
    7   chatcoin                            0x000000010edf03df __59-[DAChatDatabaseManager queryMessagesWithParams:fromTable:]_block_invoke + 191
    8   FMDB                                0x000000010eff604c __30-[FMDatabaseQueue inDatabase:]_block_invoke + 124
    
...
libc++abi.dylib: terminating with uncaught exception of type NSException
不要以为是查询数据的时候有问题, 之所以崩溃是无法反序列化, 这个原因是由于插入数据的时候造成的

因为我用的表名是 变量 不是固定的
所以需要字符串拼接
以前的代码是这样的


NSString *messageId = dict[@"messageId"];
NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:dict];

// 这个时候直接反序列化, 是没有任何问题的 
NSDictionary *message11 = [NSKeyedUnarchiver unarchiveObjectWithData:messageData];
// sql 语句 拼接参数和表名 
NSString *sql = [NSString stringWithFormat:@"INSERT  INTO %@ (userId, messageId, messageData) VALUES ('%@', '%@', '%@');", tableN, userId, messageId,messageData];
            
insertTableResult = [db executeUpdate:sql];

如果我换个固定表名, 就没有任何问题 所以 是表名 引起的问题
NSString *sql = [NSString stringWithFormat:@"INSERT  INTO t_message  (userId, messageId, messageData) VALUES ('%@', '%@', '%@');",userId, messageId,messageData];

所以这个原因 就是由于文章 开头那个链接 说明的问题
如果有 特殊字符 例如 ' 那么就会插入失败
其实我都插入成功了.... 只是反序列化不成...

修正: 按照 文章说的 方式2插入数据, 既能 动态表名,又能反序列化成功
// 方式2  当方式1 中插入的数据有特殊的字符是,就会数据写入失败,使用方式2可以完美解决这一问题。
NSString *sql001 = [NSString stringWithFormat:@"insert into %@ (userId, messageId, messageData) values (?,?,?)",tableN];

if ([db executeUpdate:sql001 withArgumentsInArray:@[ userId, messageId,messageData]]){
    NSLog(@"插入成功!");
}else{
    
    NSLog(@"插入失败");
    *rollback = YES;
    [db rollback];
    break;
}

经过测试,发现上面这个方法有个问题

因为它 需要一个数组,也就是这样

 NSArray *arr = @[conversationId,loginId, messageId,fromId, toId, messageContent, userType, messageBodyType, timestamp];

insertTableResult = [db executeUpdate:sql withArgumentsInArray:arr];

如果 toId 这个字段 为nil 那么就会崩溃, 因为你向数组中插入了一个nil 数据
而数组是以nil结尾的,
目前有个替代办法就是

id toId = messageRes.toId ? messageRes.toId : [NSNull null];

因为数组中可以插入 [NSNull null]

NSArray * array = @[@11, @15,  nil, @"1111"]; //这个写法肯定报错!

NSArray * array = @[@11, @15,  [NSNull null], @"1111"]; //这个写法就可以

但是 当你从数据库中取数据的时候, 这个时候 取的就是 [NSNull null] ,而你的 toId 这个字段 其实定义的的 NSString
取数据的时候 用这个

messageRes.toId = [set objectForColumnName:@"toId"]; // 取出为 [NSNull]
messageRes.toId = [set stringForColumn:@"toId"]; //取出为 nil 字符串


更新 解决方案 还是用 executeUpdate:
// 把数据库表名 拼接到字符串中
NSString *sql = [NSString stringWithFormat:@"insert into %@ (loginId, messageContent, userType, messageData) values (?,?,?,?)",tableN];

insertTableResult = [db executeUpdate:sql, messageRes.loginId, messageRes.messageContent, @(messageRes.userType), messageData];

FMDB的三种查询方法


 -(FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
// executeUpdate : 不确定的参数用?来占位

 // executeUpdateWithFormat : 不确定的参数用%@、%d等来占位
 // 此处一定要注意Format后面不是单纯的字符串NSString,不能随便的组装字符串,它只能接Values括号里面的占位参数,不能更改Values前面的参数,例如:
错误的表达:
 [self.db executeUpdateWithFormat:@"INSERT INTO %@ VALUES (%@, %d);", @"t_student (name, age)",name, arc4random_uniform(40)];
 正确表达:
[self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES (%@, %d);", name, arc4random_uniform(40)];

更新 附 代码
- (void)insertMessages:(NSArray *)messages toTable:(NSString *)tableName
{
    [self creatChatTableWithName:tableName];

    NSString *tableN = [NSString stringWithFormat:@"t_chat_%@", tableName];

    BOOL __block insertTableResult = NO;
    NSInteger __block insertNum = 0;
    [dataBaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        //FMDB默认情况是关闭缓存的
        [db shouldCacheStatements];
        
        for (DAChatMessageRes *messageRes in messages) {

            // conversationId  loginId messageId  fromId toId  messageContent userType messageBodyType  timestamp
            NSString *sql = [NSString stringWithFormat:@"insert or replace  into %@ (conversationId, loginId, messageId, fromId, toId, messageContent, userType, messageBodyType, timestamp) values (?,?,?,?,?,?,?,?,?)",tableN];

            insertTableResult = [db executeUpdate:sql, messageRes.conversationId,messageRes.loginId, messageRes.messageId,messageRes.fromId, messageRes.toId, messageRes.messageContent, @(messageRes.userType), @(messageRes.messageBodyType), @(messageRes.timestamp)];
            
            if (insertTableResult){
                insertNum += 1;
            }else{
                
                NSLog(@"插入失败");
                *rollback = YES;
                [db rollback];
                break;
            }
        }
        
    }];
    
    NSLog(@"插入 %zd条数据", insertNum);
    
}

你可能感兴趣的:(FMDB 数据库使用 executeUpdate)