Android Room 数据库升级

1. 版本号的位置:

@Database(entities = {LogEntity.class}, version = 1, exportSchema = false)

就是这里的 Version

以下引用自掘金:Room踩坑:理解Room的正确升库 - 掘金

感谢 咸鱼正翻身 ,写的非常清楚,不需要再改动,因此直接引用

这篇文章翻译于Google的官方博客(自备科学上网),是我踩坑后搜到的,贴出来希望大家避免掉坑里吧~

一、场景1:vesion不变 - crash

此时运行app,crash欢迎你:

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

假设我们此时增加了版本号:

@Database(entities = {User.class}, version = 2)
public abstract class UsersDatabase extends RoomDatabase

我们会遇到另一个问题:

二、场景2:增加vesion,但不提供Migration - crash

当我们把version从1改到了2。此时兴高采烈的从老版本升级上来后...crash欢迎你:

java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.

三、场景3:增加vesion,使用fallback migration - 数据被清除

不提供自定义Migration,又不想引发crash,那么可以试试这个:

database = Room.databaseBuilder(context.getApplicationContext(),
                UsersDatabase.class, "Sample.db")
        .fallbackToDestructiveMigration()
        .build();

不过,用之前请好好理解它的意思:

Room启动时将检测version是否发生增加,如果有,那么将找到Migration去执行特定的操作。如果没有因为fallbackToDestructiveMigration()。将会删除数据库并重建...

此时的确不会crash,但所有数据丢失。

四、场景4:vesion增加,提供Migration - 数据正常

如果version发生变化,即使表结构没有变化仍然需要提供Migration,那么此时应该怎么做呢?

@Database(entities = {User.class}, version = 2)
public abstract class UsersDatabase extends RoomDatabase {
// …
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // 因为没有变化,所以是一个空实现
    }
};
// …
database =  Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_1_2)
        .build();

五、场景5:改表结构

如果我们此时又想升级数据,并且这次需要更改表结构:给user表加一个last_update的字段。那么此时我们改怎么办?

1、先增加version

@Database(entities = {User.class}, version = 3)
public abstract class UsersDatabase extends RoomDatabase

2、提供Migration

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE users "
                + " ADD COLUMN last_update INTEGER");
    }
};

PS:很多老铁,看到这可能觉得头疼。还得写这么多sql。其实有一个取巧的方式,当在User.class增加完字段,编译后全局搜索3.json。你会发现sql语句已经被生成出来了。

3、修改Room.databaseBuilder

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
        .build();

大功告成,基本的升库的用法我们已经了解完了。但是这也只是基本的用法,我相信有追求的小伙伴,可能需要来点更“刺激”的才行~

六、复杂的Migration

延续我们上边的表结构,此时我们想改表:把刚才增加的Int型的last_update改成String。

我猜有经验的老司机,已经猜到需要怎么做了:

  • 创建一个新的临时表,
  • 将数据从users表复制到临时表,
  • 删了users表
  • 将临时表重命名为users

那么针对此次更新,我们需要提供这样的Migration:

static final Migration MIGRATION_3_4 = new Migration(3, 4) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // 创建临时表
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
        // 拷贝数据
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");
        // 删除老的表
        database.execSQL("DROP TABLE users");
        // 改名
        database.execSQL("ALTER TABLE users_new RENAME TO users");
    }
};

此时我们用version3版本的app升级到version4的app的确没有问题,很爽。不过我们多想一个问题:如果从version1版本的用户直接升到version4版本会怎么样?

**其实不会有问题!**Room会对这种用户一直执行1-2的Migration,2-3的Migration已经3-4的Migration。这很方便,不过如果你追求极致的性能和体验,你可以提供一个1-4的Migration,比如这样:

static final Migration MIGRATION_1_4 = new Migration(1, 4) { // 除了这一行,其他的和上边一样
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // 创建临时表
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
        // 拷贝数据
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");
        // 删除老的表
        database.execSQL("DROP TABLE users");
        // 改名
        database.execSQL("ALTER TABLE users_new RENAME TO users");
    }
};

然后把Migration加进来就ok了。

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
        .build();

以下引用自:

Android room 升级问题 - 简书

增加了一个字符串类型

database.execSQL("ALTER TABLE my_tab"
               + " ADD COLUMN configString TEXT");

增加int类型字段

database.execSQL("ALTER TABLE CallLog "
                + " ADD COLUMN e164 INTEGER");

如果你的表,保存的数据是List<String>,其实他保存就是string来,所以,增加list的时候,是增加string

database.execSQL("ALTER TABLE CallLog "
                + " ADD COLUMN list TEXT");

当你的表更换了索引你需要做的就是:
删除旧索引

//tab 表明,索引字段phones
database.execSQL("DROP INDEX IF EXISTS `index_tab_phones`");

增加新的索引

database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS index_tab_default_number ON tab(default_number)");

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值