Room踩坑:理解Room的正确升库

您所在的位置:网站首页 数据库数据丢失的原因 Room踩坑:理解Room的正确升库

Room踩坑:理解Room的正确升库

2024-04-16 10:13| 来源: 网络整理| 查看: 265

前言

最近在用Room时候,由于很大意,错误的升级数据库。导致灰度期间出现了不少crash。这篇文章就来纪念一下自己的“年少无知”吧。

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

正文

删除了一些原文中的“废话”,直接上处理代码。

有兴趣看更多细节的小伙伴,可以直接看原文。

前置条件,我们现在的app版本中已经建了这样的数据库:

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

如果我们在下一个版本的app中改了数据库的表结构,那么在不同的场景下会出现不同的问题:

一、场景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(); 尾声闲谈

首先来说Room的基本用法其实主流的ORM库并没有什么不一样。在api体验上个人感觉各有千秋吧,不过升级这一块,我觉得Room还是蛮方便的。

再聊聊官方极力“吹捧”的:对RxJava和LiveData的封装。怎么说能...个人最初的体验的确是很爽,但是一旦表结构变得复杂起来之后,由其是多表查询的时候。你会发现你的LiveData的回调会爆炸的...所以个人对这个功能持保留意见,有好用的地方也有尴尬的时候...

我自己对Room的感觉就是...还行...哈哈~

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,以及我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

个人公众号:咸鱼正翻身



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3