Iphone Sqlite应用程序内数据库迁移的最佳实践

Iphone Sqlite应用程序内数据库迁移的最佳实践,iphone,sqlite,Iphone,Sqlite,我在iphone上使用sqlite,我预计数据库模式可能会随着时间的推移而改变。每次成功迁移都需要注意哪些问题、命名约定和其他事项 例如,我曾考虑在数据库名称后面添加一个版本(例如database_v1)。如果您更改数据库模式以及在lockstep中使用它的所有代码,就像在嵌入式和手机应用程序中可能出现的情况一样,问题实际上已经得到了很好的控制(没有什么比得上在一个可能服务于数百个应用程序的企业数据库上进行模式迁移的噩梦——也不是所有应用程序都在DBA的控制之下;-)。我维护一个应用程序,该应用

我在iphone上使用sqlite,我预计数据库模式可能会随着时间的推移而改变。每次成功迁移都需要注意哪些问题、命名约定和其他事项


例如,我曾考虑在数据库名称后面添加一个版本(例如database_v1)。

如果您更改数据库模式以及在lockstep中使用它的所有代码,就像在嵌入式和手机应用程序中可能出现的情况一样,问题实际上已经得到了很好的控制(没有什么比得上在一个可能服务于数百个应用程序的企业数据库上进行模式迁移的噩梦——也不是所有应用程序都在DBA的控制之下;-)。

我维护一个应用程序,该应用程序需要定期更新sqlite数据库,并将旧数据库迁移到新模式,我的工作如下:

为了跟踪数据库版本,我使用sqlite提供的内置用户版本变量(sqlite不处理此变量,您可以随意使用)。它从0开始,您可以使用以下sqlite语句获取/设置此变量:

> PRAGMA user_version;  
> PRAGMA user_version = 1;
当应用程序启动时,我检查当前用户版本,应用使架构更新所需的任何更改,然后更新用户版本。我将更新打包到事务中,以便在出现任何错误时,不会提交更改


对于进行架构更改,sqlite支持某些操作(重命名表或添加列)的“ALTER TABLE”语法。这是一种就地更新现有表的简单方法。请参阅此处的文档:。以删除“ALTER TABLE”不支持的列或其他更改语法中,我创建一个新表,将日期迁移到其中,删除旧表,并将新表重命名为原始名称。

Just Guilly的答案完全正确(你明白我的意思!),我们使用它跟踪应用程序中当前数据库模式的版本

要运行需要进行的迁移,以获得与应用程序预期架构版本匹配的用户版本,我们使用switch语句。下面是一个在我们的应用程序中的示例:


IMO最好的解决方案是构建一个SQLite升级框架。我也有同样的问题(在C#world),我也构建了自己的框架。你可以阅读它。它工作得非常完美,我的升级(以前是噩梦般的)只需付出最少的努力

尽管该库是用C#实现的,但在您的案例中,这里提出的想法也应该很好地发挥作用。

一些技巧

1) 我建议将所有用于迁移数据库的代码放入NSO操作中,并在后台线程中运行它。迁移数据库时,可以使用微调器显示自定义UIAlertView

2) 确保您正在将数据库从捆绑包复制到应用程序的文档中,并从该位置使用它,否则每次应用程序更新都会覆盖整个数据库,然后迁移新的空数据库

3) FMDB很棒,但由于某些原因,它的executeQuery方法无法执行PRAGMA查询。如果要使用PRAGMA user_version检查模式版本,则需要编写自己的方法,直接使用sqlite3

4) 此代码结构将确保您的更新按顺序执行,并且无论用户在应用程序更新之间间隔多长时间,都会执行所有更新。它可以进一步重构,但这是一种非常简单的方法。每次实例化数据单例时,都可以安全地运行此方法,并且只要正确设置数据单例,每次会话只需执行一次微小的db查询

- (void)upgradeDatabaseIfNeeded {
    if ([self databaseSchemaVersion] < 3)
    {
        if ([self databaseSchemaVersion] < 2)
        {
            if ([self databaseSchemaVersion] < 1)
            {
                // run statements to upgrade from 0 to 1
            }
            // run statements to upgrade from 1 to 2
        }
        // run statements to upgrade from 2 to 3

        // and so on...

        // set this to the latest version number
        [self setDatabaseSchemaVersion:3];
    }
}
-(void)升级数据库如果需要{
if([自数据库架构]<3)
{
if([自数据库架构]<2)
{
if([自数据库架构]<1)
{
//运行语句以从0升级到1
}
//运行语句以从1升级到2
}
//运行语句以从2升级到3
//等等。。。
//将此设置为最新版本号
[self-SetDatabaseSchemaversation:3];
}
}

让我与FMDB和MBProgressHUD共享一些迁移代码

下面是如何读取和写入模式版本号(这可能是模型类的一部分,在我的示例中,它是一个名为Database的单例类):

下面是一个懒洋洋地打开数据库的
[self-database]
方法:

- (FMDatabase *)database {
    if (!_databaseOpen) {
        _databaseOpen = YES;

        NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *databaseName = [NSString stringWithFormat:@"userdata.sqlite"];

        _database = [[FMDatabase alloc] initWithPath:[documentsDir stringByAppendingPathComponent:databaseName]];
        _database.logsErrors = YES;

        if (![_database openWithFlags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FILEPROTECTION_COMPLETE]) {
            _database = nil;
        } else {
            NSLog(@"Database schema version is %d", [self databaseSchemaVersion]);
        }
    }
    return _database;
}
以下是从视图控制器调用的迁移方法:

- (BOOL)databaseNeedsMigration {
    return [self databaseSchemaVersion] < databaseSchemaVersionLatest;
}

- (void)migrateDatabase {
    int version = [self databaseSchemaVersion];
    if (version >= databaseSchemaVersionLatest)
        return;

    NSLog(@"Migrating database schema from version %d to version %d", version, databaseSchemaVersionLatest);

    // ...the actual migration code...
    if (version < 1) {
        [[self database] executeUpdate:@"CREATE TABLE foo (...)"];
    }

    [self setDatabaseSchemaVersion:DatabaseSchemaVersionLatest];
    NSLog(@"Database schema version after migration is %d", [self databaseSchemaVersion]);
}

1
。使用基于SQL的迁移列表创建
/migrations
文件夹,其中每个迁移如下所示:

/migrations/001 categories.sql

-- Up
CREATE TABLE Category (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Category (id, name) VALUES (1, 'Test');

-- Down
DROP TABLE User;
-- Up
CREATE TABLE Post (id INTEGER PRIMARY KEY, categoryId INTEGER, text TEXT);

-- Down
DROP TABLE Post;
/migrations/002 posts.sql

-- Up
CREATE TABLE Category (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Category (id, name) VALUES (1, 'Test');

-- Down
DROP TABLE User;
-- Up
CREATE TABLE Post (id INTEGER PRIMARY KEY, categoryId INTEGER, text TEXT);

-- Down
DROP TABLE Post;
2
。创建包含应用迁移列表的db表,例如:

CREATE TABLE Migration (name TEXT);
3
。更新应用程序引导逻辑,以便在启动之前,它从
/migrations
文件夹中获取迁移列表,并运行尚未应用的迁移


下面是一个使用JavaScript实现的示例:

对于.net,您可以使用lib:


它很简单,因此对于任何其他平台,您都可以轻松实现与lib中相同的行为。

这是一个很好的工具;太糟糕了,它不是免费的,我尝试使用相同的逻辑,但由于某种原因,当我以编程方式执行“pragma user_version=?”时,它失败了。。。知道吗?pragma设置不支持参数,您必须提供实际值:“pragma user_version=1”。我有一个问题。比如说,如果您创建了初始版本1。目前的版本是5。版本2、3、4中有一些更新。最终用户只下载了您的版本1,现在升级到版本5。应该怎么做?分几个步骤更新数据库,应用从版本1到版本2再到版本2所需的更改
CREATE TABLE Migration (name TEXT);