使用Migration升级Room数据库

Migration升级数据库

有时需要更改现有的数据库架构。如果我们将添加,更新或删除数据库中的某些字段然后运行我们的应用程序,我们将看到来自Room的异常:

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.
复制代码

Room 会发现它的架构已经改变,并且它与数据库版本不再一致。现在,如果我们将增加版本号并再次运行应用程序,我们将看到另一个异常:

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.
复制代码

现在Room不知道如何将数据库从版本1迁移到版本2.在此错误房间建议我们两个解决方案:

  • 删除并重新创建整个数据库
  • 将现有的数据库架构升级到较新的版本

第一个选项是最简单的选项,但它不是最好的用户,因为我们将清除整个数据库...

不推荐这种解决方案,但可以使用。为此,我们需要在数据库构建器中添加一行:

Room.databaseBuilder(context, RepoDatabase.class, DB_NAME)
    .fallbackToDestructiveMigration()
    .build();
复制代码

但是,如果我们希望我们的应用程序更加用户友好,我们可以提供数据库的迁移。使用Room 也很容易实现。为此,我们需要在数据库构建器中声明Migration:

举个例子:

旧版本的数据库结构是:

@Entity
public class Repo {
    @PrimaryKey
    public int id;
    public String name;
    public String url;

    public Repo() {
    }

    public Repo(int id, String name, String url) {
        this.id = id;
        this.name = name;
        this.url = url;
    }
    ... 省略getter setter  tostring 放法 
}

复制代码

新的数据结构想加入 age 字段来表示年龄,添加后的代码:

@Entity
public class Repo {
    @PrimaryKey
    public int id;
    public String name;
    public String url;
    //新添加的age 字段
    public int age;

    public Repo() {
    }

    public Repo(int id, String name, String url, int age) {
        this.id = id;
        this.name = name;
        this.url = url;
        this.age = age;
    }


复制代码

然后创建:

private static Migration migration1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE Repo"
                + " ADD COLUMN age INTEGER ");
     
    }
};

复制代码

添加 migration1_2 并增加数据库版本号为2:

private static RepoDatabase create(final Context context) {
    return Room.databaseBuilder(
            context,
            RepoDatabase.class,
            DB_NAME)
            .addMigrations(migration1_2)
            .build();
}
// 修改版本号
@Database(entities = Repo.class, version = 2)
复制代码

调试运行看是否升级成功: 首先我在旧的版本数据库表里添加了一条数据:

[Repo{id=1, name='', url='Tomurl'}]
复制代码

当运行成功获取数据库数据的时候出现了异常:

 Caused by: java.lang.IllegalStateException: Migration didn't properly handle Repo(com.meevii.roomlibrarydemo.data.Repo). 
  Expected:   

  TableInfo{name='Repo',  
  columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0},   
  id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1},  
  age=Column{name='age', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0},    
  url=Column{name='url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}},    
  foreignKeys=[], indices=[]}    

  Found:  

  TableInfo{name='Repo',  
  columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0},   
  id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1},   
  age=Column{name='age', type='INTEGER',  affinity='3', notNull=false, primaryKeyPosition=0},  
  url=Column{name='url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}},   
  foreignKeys=[], indices=[]}    

   at com.meevii.roomlibrarydemo.data.RepoDatabase_Impl$1.validateMigration(RepoDatabase_Impl.java:73)
复制代码

分析问题: 跟进代码的发现:

final TableInfo _infoRepo = new TableInfo("Repo", _columnsRepo, _foreignKeysRepo, _indicesRepo);
final TableInfo _existingRepo = TableInfo.read(_db, "Repo");
if (! _infoRepo.equals(_existingRepo)) {
  throw new IllegalStateException("Migration didn't properly handle Repo(com.meevii.roomlibrarydemo.data.Repo).\n"
          + " Expected:\n" + _infoRepo + "\n"
          + " Found:\n" + _existingRepo);
}

复制代码

当已经存在的数据表跟新的数据表不同时会抛出此异常: 在看上面的错误信息发现:

Expected  
  age=Column{name='age', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0},     
Found
  age=Column{name='age', type='INTEGER',  affinity='3', notNull=false, primaryKeyPosition=0},   

 notNull  的值不同  
复制代码

经过一番查找找到了解决办法:


private static Migration migration1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE Repo"
                + " ADD COLUMN age INTEGER NOT NULL DEFAULT 10");
       //当添加int 类型数据时,需要添加默认值
    }
};

复制代码

再次运行获取数据库数据:

[Repo{id=1, name='', url='Tomurl', age='10'}]
复制代码

参考链接: stackoverflow.com/questions/4…
android.jlelse.eu/android-arc…

转载于:https://juejin.im/post/5b914a18f265da0ad82be9f3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值