应用关闭并重新启动后,SQLite数据库的某些更改不会保留

时间:2009-05-13 12:11:41

标签: iphone objective-c sqlite

我有一个正在开发的iPhone应用程序,当应用程序启动时,我打开一个SQLite数据库连接,并在应用程序终止时关闭它。数据库位于应用程序文档文件夹中。

在应用的生命周期内,我会运行多个INSERTUPDATE语句。但是,由于某些原因,一旦应用程序关闭然后重新启动,我的UPDATE语句就无法正常保存。我正在调用sqlite3_prepare_v2sqlite3_stepsqlite3_finalize,所以我确定交易应该最终确定。我已经在所有这三个函数上添加了断点,它们都被点击了,并且都返回了正确的值。

当我在更新后关闭应用程序并查看文档目录后,我可以看到另一个带有-journal的数据库文件。这是否意味着某些事情在交易方面不正常?

我还注意到,在我使用applicationWillTerminate:关闭连接的sqlite3_close()方法中,它正在返回SQLITE_BUSY。为什么会这样?

我一直在苦苦挣扎了一整天,所以我真的希望有人能指出我正确的方向!

这是执行更新查询的代码:

// Update statement
sqlite3_stmt *stmt;
char *query = "UPDATE records SET fielda = ? WHERE pkfield = ?;";
if (sqlite3_prepare_v2(database, query, -1, &stmt, nil) == SQLITE_OK &&

   // Bind
   sqlite3_bind_text(stmt, 1, [myNSStringVar UTF8String], -1, nil) == SQLITE_OK &&
   sqlite3_bind_int(stmt, 2, recordID) == SQLITE_OK) {

   // Execute
   int ret = sqlite3_step(stmt);
   if (ret == SQLITE_DONE) {
      sqlite3_finalize(stmt);
   }
}

4 个答案:

答案 0 :(得分:2)

啊......我明白了!我的一个模糊的敲定声明从未实现过!因为它只是一个SELECT语句,所以我没有检查它,因为它与任何数据操作或事务无关!那是一天开窗的发展!但我不会再犯这个错误了!

无论如何,谢谢你的帮助!

答案 1 :(得分:1)

如果您正在重用update语句,则应在使用后立即调用语句上的sqlite3_reset(),调用sqlite3_clear_bindings()也可能是合适的,以确保重置已分配的参数。然后,您应该只在清理时调用sqlite3_finalize。这可能是您的问题,因为如果语句仍在执行并且您在其上调用sqlite3_finalize,则执行将被中断。也就是说,如果你还没有,你应该最有可能重复使用声明来提高效率。对我而言,它的工作原理如下:

static sqlite3_stmt *update_statement = nil;

-(void)performUpdateOnRow: (NSInteger)primaryKey {

    if (update_statement == nil){
        const char *sql = "UPDATE foo SET bar=?,baz=? WHERE pk=?";
        if( sqlite3_prepare_v2(database, sql, -1, &update_statement, NULL) != SQLITE_OK ){
                // Insert some serious error handling here!
        }
    }
    sqlite3_bind_text(update_statement, 1, @"first", -1,SQLITE_TRANSIENT);
    sqlite3_bind_text(update_statement, 2, @"second", -1,SQLITE_TRANSIENT); 
    sqlite3_bind_int(update_statement, 3, primaryKey);

    sqlite3_step(update_statement);
    sqlite3_reset(update_statement);

}

答案 2 :(得分:1)

看起来你需要让sqlite在杀死你的应用程序之前完成它的当前任务 - SQLITE_Busy意味着"the database file is locked",所以关注the docs here应该让你走上正确的道路。

答案 3 :(得分:1)

您确定在应用程序使用期间为SQLite数据库保存了大量内容对性能最佳吗?

我倾向于在运行时在NSMutableArray中存储更改等,每次更新对象时都将“脏”标志设置为YES。

然后在我的applicationWillTerminate方法中,我调用以下内容:

[todos makeObjectsPerformSelector:@selector(dehydrate)];

我的“脱水”方法(这是基于Apple自己的操作SQLite数据库的指南)基本上是这样的:

- (void) dehydrate {
if (dirty) {
    if (dehydrate_statement == nil) {
        const char *sql = "UPDATE todo SET text=?,priority=?,complete=?,detail=? WHERE pk=?";
        if (sqlite3_prepare_v2(database,sql,-1,&dehydrate_statement,NULL) != SQLITE_OK) {
            NSAssert1(0,@"Error: failed to prepare statement with message '%s'.",sqlite3_errmsg(database));
        }
    }

    sqlite3_bind_int(dehydrate_statement, 5, self.primaryKey);
    sqlite3_bind_text(dehydrate_statement, 4, [self.detail UTF8String],-1,SQLITE_TRANSIENT);
    sqlite3_bind_int(dehydrate_statement, 3, self.status);
    sqlite3_bind_int(dehydrate_statement, 2, self.priority);
    sqlite3_bind_text(dehydrate_statement, 1, [self.text UTF8String],-1,SQLITE_TRANSIENT);
    int success = sqlite3_step(dehydrate_statement);

    if (success != SQLITE_DONE) {
        NSAssert1(0,@"Error: failed to save changes with message '%s'.", sqlite3_errmsg(database));
    }

    sqlite3_reset(dehydrate_statement);
    dirty = NO;
    }
}

这有帮助吗?