美文网首页开发指南
Room 数据库升级遇到的问题: Migration didn'

Room 数据库升级遇到的问题: Migration didn'

作者: 星月下的青草 | 来源:发表于2018-09-13 23:06 被阅读0次

    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'}]
    

    参考链接:
    https://stackoverflow.com/questions/46372036/room-database-migration-doesnt-properly-handle-alter-table-migration
    https://android.jlelse.eu/android-architecture-components-room-migration-1a269e1aeef7

    相关文章

      网友评论

        本文标题:Room 数据库升级遇到的问题: Migration didn'

        本文链接:https://www.haomeiwen.com/subject/ylxqgftx.html