objective-c sqlite3 数据库更改不是持久的

Posted

技术标签:

【中文标题】objective-c sqlite3 数据库更改不是持久的【英文标题】:objective-c sqlite3 database changes not persistent 【发布时间】:2013-09-24 11:42:40 【问题描述】:

我的应用程序有一个 SQLite 数据库。要从数据库中检索实体,我使用此方法:

- (sqlite3*) openDatabaseNamed:(NSString*)databaseName

    if(![databaseName isEqualToString:kTopInternationalDatabaseName] &&
       ![databaseName isEqualToString:kTop500RODatabaseName])
        NSAssert(nil, @"Database does not exist!");
    
    sqlite3 * dataBase;
    NSString * path;
    path =  [[NSBundle mainBundle] pathForResource:databaseName ofType:@"sqlite3"];
    if (sqlite3_open([path UTF8String], &dataBase) != SQLITE_OK) 
        NSString * errorString = [NSString stringWithFormat:@"[SQLITE] Unable to open database <%@> ",databaseName];
        NSAssert(nil,errorString);
    
    return dataBase;


- (NSArray *) getAllEntitiesForDatabaseNamed:(NSString*)databaseName

  (...)
    sqlite3 * database  = [self openDatabaseNamed:databaseName];

    NSMutableArray *retval = [[NSMutableArray alloc] init];
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@",databaseName];

    NSArray *properties = [entityClass classProperties];

    if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
        == SQLITE_OK) 
        while (sqlite3_step(statement) == SQLITE_ROW) 


            for (int i=2; i<countLimit; i++)

                chars = (char *) sqlite3_column_text(statement,i+1);
                if(chars != NULL)
                
                    containerString = [NSString stringWithUTF8String:chars];
                    if(containerString && containerString.length>0)
                        [entityModel setValue:containerString forKey:properties[i]];
                    
                
            
            [retval addObject:entityModel];
        
        sqlite3_finalize(statement);
    
    sqlite3_close(database);
    return retval.copy;

一切都按预期进行。为了将实体的自定义字段设置为数据库中的特定值,我使用以下方法:

- (void)setEntity:(EntityModel *)entity favorite:(BOOL)favorite

    NSString *query = [NSString stringWithFormat:@"UPDATE %@ SET favorite = %i WHERE position = '%i';",kDatabaseName,favorite?1:0,entity.positionInTop];

    sqlite3_stmt *statement;
    sqlite3 * database = [self openDatabaseNamed:kTop500RODatabaseName];

    if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
        == SQLITE_OK) 
        sqlite3_step(statement);
        sqlite3_finalize(statement);
    
    sqlite3_close(database);

发生的事情有点奇怪。如果我使用更新方法并在应用程序查询的同一生命周期中使用getAllEntitiesForDatabaseNamed 对所有实体进行查询,那么我对setEntity:Favorite: 所做的更改将持续存在。另一方面,如果我使用更新方法,然后关闭应用程序并重新启动它,我使用setEntity:Favorite: 所做的更改将丢失。为什么会这样?

PS:我也试过使用sqlite3_exec,结果还是一样:

if (sqlite3_exec(database, [query UTF8String], NULL, NULL, NULL) != SQLITE_OK) 
    // deal with error...
    NSLog(@" -- ERROR!");

【问题讨论】:

【参考方案1】:

问题是您在捆绑中打开数据库,该数据库是只读的(至少在设备上)。相反,您应该检查数据库是否存在于您的 Documents 文件夹中,如果不存在,请将其从捆绑包中复制到那里,然后从 Documents 文件夹中打开数据库。

因此,您可能会执行以下操作:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:databaseName ofType:@"sqlite"];

NSString *documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *documentsPath   = [[documentsFolder stringByAppendingPathComponent:databaseName] stringByAppendingPathExtension:@"sqlite"];

NSFileManager *fileManager = [NSFileManager defaultManager];

if (![fileManager fileExistsAtPath:documentsPath]) 
    NSError *error = nil;
    BOOL success = [fileManager copyItemAtPath:bundlePath toPath:documentsPath error:&error];
    NSAssert(success, @"%s: copyItemAtPath failed: %@", __FUNCTION__, error);

完成后,您现在可以继续在documentsPath 打开数据库。

【讨论】:

你能详细说明一下吗? 我相信虽然断言是错误的,并且当复制成功时它会崩溃应用程序。 还有其他可能导致此问题的原因吗?我有同样的问题。 sqlite3_prepare 语句返回 1 但是我已经验证我正在从 Documents 目录中提取数据库文件。我需要设置某种权限吗?我可以使用 firefox sqlite_manager 工具访问数据库,并且我的 SQL 语句在那里工作。 似乎我缺少与主包中原始数据库文件有关的内容。如果我将我的 database.sqlite 复制到 Documents 目录,我没有问题。但是如果我让文件管理器这样做,我会得到一个空文件。 所以我设法让我的 db 文件不包含在 bundle 中。为了解决这个问题,我去了“构建阶段”并将 .sqlite 文件添加到“复制捆绑资源”

以上是关于objective-c sqlite3 数据库更改不是持久的的主要内容,如果未能解决你的问题,请参考以下文章

Sqlite3 奇怪的行为

SQLite3 Javascript |更改数据库中每一行的数据

Objective-C访问SQLite

如何将日期+时间保存和检索到 sqlite3 数据库中?

在迁移中更改自动增量值(PostgreSQL 和 SQLite3)

IOS-SQLite数据库使用详解