0

在我的应用程序中,我将一些数据保存在本地 Sqlite 数据库中。Sqlite 打开,插入一切正常。但是当我尝试更新特定行/记录的数据时,如果失败并显示 ASSERTION FAILURE 消息..

* -[gss_databaseHandler updateRecord::::] 中的断言失败,/Users/gss/Desktop/SalesPro copy/SalesPro/../gss_databaseHandler.m:210

NSString *databasePath;
// Method to open a database, the database will be created if it doesn't exist
-(void)initDatabase
{
// Create a string containing the full path to the sqlite.db inside the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
databasePath = [documentsDirectory stringByAppendingPathComponent:@"contact1"];

// Check to see if the database file already exists
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];

// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
    // Create the database if it doesn't yet exists in the file system
    if (!databaseAlreadyExists)
    {
        // Create the contactList table
        const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
        char *error;
        if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
        {
                NSLog(@"Database and tables created.");
        }
        else
        {
                NSLog(@"Error: in creating/opening database");
        }

    }
  }
}


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

[self initDatabase];

sqlite3_stmt *updateStmt = nil;

    if(updateStmt == nil) {
        const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";

     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_finalize(updateStmt);
sqlite3_close(databaseHandle);

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

}

请帮忙!..

4

2 回答 2

1

所以,有几个想法:

  1. 正如 Prateek 和 Lefteris 建议的那样,您应该更改bind调用的顺序,以便它们与 SQL 中字段的顺序相匹配。它们也以索引一开始,而不是零。我总是觉得奇怪的是sqlite3_bind调用索引从一开始,而sqlite3_column调用索引从零开始,但这就是它的工作方式。

  2. 虽然这可能没问题,但如果你曾经做过断言,你通常应该确保首先 (a) 完成任何成功准备的语句;(b) 关闭数据库。您不想冒险让您的数据库处于不一致的状态。

  3. 你可能不应该释放sapContactIdsapCustId在这里。你没有retain或没有做任何事情来增加他们的保留数量,所以在这里释放他们可能是不谨慎的。如果您通过静态分析器运行代码(按shift++或从产品菜单中选择“分析” commandB,您无疑会在那里看到建议/警告。

  4. 正如官方 SQLite An Introduction To the SQLite C/C++ Interfaceopen中所述,您的、prepare_v2stepfinalize和的顺序close是完全正确的。你不需要打电话sqlite3_reset。的目的reset让你重用以前准备好的sqlite3_stmt,但你在这里没有这样做,所以reset这里没有必要并且什么都没有。

  5. 在您的评论中,您曾说过您收到“数据库锁定”错误。如果您在解决上述问题后仍然得到它,请告诉我们您从哪里得到它(在open?在prepareSQL 语句期间?),因为这个问题可能有不同的来源。当然,多线程数据库操作会导致这种情况。您需要通过准确地向我们展示您在哪一行收到此锁定消息来帮助我们诊断此问题,并描述您可能正在执行的其他数据库操作(除了原始问题中的代码),特别是如果您正在执行任何操作后台队列/线程。

    解决“数据库锁定”问题的挑战在于,问题始终不在于您遇到“数据库锁定”错误的代码,而在于您未能正确处理的一些先前的数据库交互。从技术上讲,调用失败sqlite3_finalize可能会导致“数据库锁定”问题,但实际上这种情况很少发生。我看到“数据库锁定”错误通常是由于其他问题而发生的,但我们真的需要知道您是在打开数据库期间遇到它,还是在尝试prepareSQLstep语句时遇到它。你需要告诉我们哪个让我们在诊断中走得更远。但是,就像我说的,问题无疑在于其他/先前的数据库交互,“数据库锁定”错误之前。


如果您对有关原始问题中代码重写的讨论感兴趣,请参见下文。

一个小建议,但我建议更改您的initDatabase方法以确保在sqlite3_open调用失败时记录错误消息,例如:

-(void)initDatabase
{
    // Create a string containing the full path to the sqlite.db inside the documents folder
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"contact1.sqlite"];
    
    // Check to see if the database file already exists
    bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
    
    // Open the database and store the handle as a data member
    if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
    {
        // Create the database if it doesn't yet exists in the file system
        if (!databaseAlreadyExists)
        {
            // Create the contactList table
            const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
            char *error;
            if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
            {
                NSLog(@"Database and tables created.");
            }
            else
            {
                NSLog(@"%s: error creating table: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
            }
        }
    }
    else
    {
        NSLog(@"%s: error opening database: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
    }
}

其次,你看insertRecord

  • 您想修复使用四个绑定语句的顺序。

  • 您还可以消除冗余if语句。

  • 如果您使用断言或从方法中间返回进行数据库交互,您总是希望进行优雅的清理。例如,你会看到我的准备是否失败,我会直接关闭,但如果step失败,那么我确保finalizeand close

  • 您可能应该摆脱那些不恰当的release语句,将它们移到代码中的正确位置。

  • 您应该将方法签名更改为具有命名参数(因为在编码指南的一般规则部分, Apple 说我们应该命名我们的方法关键字。

因此:

- (void) insertRecord:(int)recordID
            sapCustId:(NSString *)sapCustId
         sapContactId:(NSString *)sapContactId
            timestamp:(NSString *)timestamp
{
    [self initDatabase];
    
    sqlite3_stmt *statement = nil;
    
    const char *sql_stmt = "INSERT INTO contactList (sapCustId, sapContactId, timestamp, record_ID) VALUES (?, ?, ?, ?)";
    
    if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &statement, NULL) != SQLITE_OK)
    {
        NSLog(@"%s: insert prepare error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
        sqlite3_close(databaseHandle);
        NSAssert(0, @"insert prepare error");
    }
    
    sqlite3_bind_text(statement, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(statement, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(statement, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(statement, 4, recordID);
    
    if (sqlite3_step(statement) != SQLITE_DONE)
    {
        NSLog(@"%s: insert step error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
        sqlite3_finalize(statement);
        sqlite3_close(databaseHandle);
        NSAssert(0, @"insert step error");
    }
    
    sqlite3_finalize(statement);
    sqlite3_close(databaseHandle);
}
于 2013-01-17T22:34:05.743 回答
0

试试这个

您缺少 sqlite3_reset,编号应从 1 开始

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

[self initDatabase];

sqlite3_stmt *updateStmt = nil;

    if(updateStmt == nil) {
        const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";

     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, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(updateStmt, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(updateStmt, 4, recordID);

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

sqlite3_reset(updateStmt);
sqlite3_finalize(updateStmt);
sqlite3_close(databaseHandle);

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

}

希望这可以帮助你..

编辑 :-

您尚未添加sqlite3_finalize();任何以前的功能,因此您收到数据库锁定错误..

于 2013-01-17T10:06:56.983 回答