我的应用程序使用SQLite数据库存储联系人的某些数据。例如(record_ID,时间戳等)。我正在尝试使用此方法使用某些值更新SQLITE Records。

初始化数据库后,我习惯调用此方法。仅更新1条记录。即使我每次都调用此方法进行更新。

- (void) updateRecord:(int)recordID:(NSString *)sapCustId:(NSString *)sapContactId: (NSString *)timestamp {


static sqlite3_stmt *updateStmt = nil;

    if(updateStmt == nil) {
        NSString *sql = [NSString stringWithFormat:@"update contactList set sapCustId = \"%@\", sapContactId = \"%@\", timestamp = \"%@\" Where record_ID = \"%d\"", sapCustId, sapContactId, timestamp, recordID];

        const char *sql_stmt = [sql UTF8String];

        if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &updateStmt, NULL) != SQLITE_OK)
            NSAssert1(0, @"Error while creating update statement. '%s'", sqlite3_errmsg(databaseHandle));
    }

//        sqlite3_bind_text(updateStmt, 0, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
//        sqlite3_bind_text(updateStmt, 1, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
//        sqlite3_bind_int(updateStmt, 2, recordID);
//        sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);

    if(SQLITE_DONE != sqlite3_step(updateStmt))
        NSAssert1(0, @"Error while updating. '%s'", sqlite3_errmsg(databaseHandle));

sqlite3_reset(updateStmt);
sqlite3_close(databaseHandle);

//Reclaim all memory here.
[sapContactId release];
[sapCustId release];

}


让我知道。如何解决?

最佳答案

关键问题是您的代码仅构建一次updateStmt,因为这是一个static,并且您正在检查它是否不是nil。因此,第一个SQL语句正在构建,而其余的都不是。最简单的解决方法是摆脱static限定词和if(updateStmt == nil) ...逻辑,您应该参加比赛。另外,请确保将sqlite3_reset替换为sqlite3_finalize

此代码明确用于执行的static模式(重复使用sqlite3_stmt,每次迭代通过?将SQL中的sqlite3_bind_xxx占位符与新值绑定,在每次后续迭代之前执行sqlite3_reset),如果您使用完全相同的SQL语句,但每次迭代都只绑定新值。但是看起来您已经注释掉了代码的那部分,并从SQL中删除了?占位符,而是使用stringWithFormat构建SQL。

最简单的解决方案是退出static模式(摆脱static关键字,取消检查以查看是否为nil,并使用sqlite3_finalize代替sqlite3_reset),您应该可以。



其他想法:


好像您每次更新单行都在关闭数据库。如果要更新一堆记录,则可能要考虑保持数据库打开状态。即使您决定打开和关闭数据库,也可以在代码中的相同逻辑级别(在updateRecord方法内部或在updateRecord方法外部)进行相同的操作。它只是使代码更直观。
假设您决定不想重复使用先前准备的SQL语句来执行多个UPDATE语句,这将需要使用SQLite bind函数调用,这并不意味着您不应该这样做。无论如何,请考虑使用那些bind函数调用。仅通过stringWithFormat构建SQL看起来更方便,但通常最好使用sqlite3_bind_xxx调用,因为(a)保护您免受注入攻击; (b)您不必担心转义参数值(例如其中带有引号的情况)等。

不要养成使用stringWithFormat构建SQL的习惯,因为一般来说它很脆弱,在某些情况下很危险。
在您的SQL中,通常不会在数字字段值周围使用引号。
如果您决定尝试使static sqlite3_stmt / sqlite3_reset逻辑起作用,请注意两个小问题:


我不会在数据会话之间使用相同的sqlite3_stmt(即,不要关闭数据库然后重新打开它)。也许可以,但是考虑到数据库是sqlite3_prepare_v2的参数,我不会做任何这样的假设。
最后,关闭数据库后,您实际上应该在sqlite3_finalize上调用sqlite3_stmt,然后再次将其设置回nil

10-07 19:23
查看更多