对“升级到Room 2.2.0时处理列默认值”感到困惑吗?

时间:2019-07-15 10:25:20

标签: android android-room

我指的是https://developer.android.com/training/data-storage/room/migrating-db-versions.md#handle-default-values-migrations

我对指南感到困惑。我的理解是

如果您添加NON NULL新列

@Entity
public class Song {
    // ...
    @NonNull
    final String tag;
}

使用以下ALTER TABLE迁移策略是错误的


static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL(
            "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''");
    }
};

您需要使用以下drop and re-create迁移策略

正确

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE new_Song (" +
                "id INTEGER PRIMARY KEY NOT NULL," +
                "name TEXT," +
                "tag TEXT NOT NULL DEFAULT '')");
        database.execSQL("INSERT INTO new_Song (id, name, tag) " +
                "SELECT id, name, tag FROM Song");
        database.execSQL("DROP TABLE Song");
        database.execSQL("ALTER TABLE new_Song RENAME TO Song");
    }
};

这甚至使我感到困惑。难道这两种方法都不会具有相同的列-tag TEXT NOT NULL DEFAULT ''吗?前一种方法如何标记为有问题的方法?

有人可以提供一个简单的示例,来说明使用ALTER TABLE可能会发生什么问题,以及drop and re-create如何克服该问题吗?

谢谢。

2 个答案:

答案 0 :(得分:0)

请注意,该示例引用了两个模式版本(1和2),但是房间版本不同(2.2.0之前且等于或大于)。他们的关键是在这些句子中:

  

但是,2.2.0之前的Room版本没有意识到这种更改( aka no @ColumnInfo ),从而导致具有全新安装的应用程序用户与从版本1迁移到2的用户。具体地说,版本2的新创建的数据库将不包含默认值。

这些句子告诉我们,您有一个使用Room 2.1.x或更低版本实施的应用,该应用没有@ColumnInfo ,并且您已经已经升级了ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT将您的架构升级到版本2。此时,您将获得一个新的用户安装,该安装允许Room 2.1.x创建模式2的版本,而没有默认值(因为@ColumnInfo不存在)。

现在,用户已使用@ColumnInfo升级到Room 2.2.0的二进制文件,并且Room将抱怨您的一个用户子集的数据库哈希值不同。因此,仅对于使用Room 2.1.x或在不使用默认值的情况下创建其模式2版本的用户,才需要使用文档中概述的MIGRATION_2_3。因此,对于通过手册ALTER TABLE从版本1迁移到版本2的用户来说,这是一个 dummy 更新。但是,对于某些用户来说,执行 nop 比开始在升级代码路径中添加条件逻辑来尝试检测实际何时需要更改要容易得多。

另一种改写方式是,开发人员使用手写的ALTER TABLE 列的默认值来将数据库升级到与一个Room不同的模式,这将从头开始创建

答案 1 :(得分:0)

经过与原始作者讨论后 https://issuetracker.google.com/issues/137515134,这是一个简单易懂的示例。


版本1,2.1.0室

@Entity
public class Song {
    @PrimaryKey
    final long id;
}

版本2,2.1.0室(添加了@NonNull)

@Entity
public class Song {
    @PrimaryKey
    final long id;
    @NonNull
    final String tag;
}

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL(
            "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''");
    }
};

版本3,会议室2.2.0(会议室已升级到2.2.0,已添加@ColumnInfo)

@Entity
public class Song {
    @PrimaryKey
    final long id;
    @ColumnInfo(defaultValue = "")
    @NonNull
    final String tag;
}

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE new_Song (" +
                "id INTEGER PRIMARY KEY NOT NULL," +
                "tag TEXT NOT NULL DEFAULT '')");
        database.execSQL("INSERT INTO new_Song (id, tag) " +
                "SELECT id, tag FROM Song");
        database.execSQL("DROP TABLE Song");
        database.execSQL("ALTER TABLE new_Song RENAME TO Song");
    }
};

读者注意事项

  1. 数据库版本2(从未经历过MIGRATION_1_2的数据库)在其SQLite模式中没有DEFAULT。 MIGRATION_2_3是必需的。

  2. 数据库版本2(已通过MIGRATION_1_2)在其SQLite模式中具有DEFAULT。 MIGRATION_2_3无害。

  3. 在SQLite中,无法更改列以添加DEFAULT约束。