Room的数据迁移踩坑

最近使用Room进行版本提升,要在表中删除一条字段。新增字段的方式之前已经使用过了:

val goodsDatabase = Room.databaseBuilder(
                            context.applicationContext, 
                            GoodsDatabase::class.java, 
                            "goods")
                .allowMainThreadQueries()
                .addMigrations(object : Migration(2, 3) {
                    override fun migrate(database: SupportSQLiteDatabase) {
                        database.execSQL("ALTER TABLE stock_goods ADD goods_discount INTEGER NOT NULL DEFAULT 100")
                })
                .build()

很简单,利用migration可以通过sql语句在stock_goods 这个表中新增一个Integer类型的goods_discount字段,其中Migration(2, 3)中的2和3分别是当前数据库的版本升级的数据库的版本

然后我以为删除字段和新增字段一样,只要将sql语句改为“ALTER TABLE stock_goods DROP COLUMN goods_model”就可以了(goods_model是我要新增的字段)。结果直接提示错误,网上一查才知道原来room是不能直接删除数据库中的字段的,因此我们只能另外想办法了。

//                        创建一个新的表并且复制数据到新表中
                        database.execSQL(
                            "CREATE TABLE new_stock_goods (goods_barcode TEXT NOT NULL," +
                                    " goods_name TEXT NOT NULL, goods_price REAL NOT NULL, id INTEGER NOT NULL, goods_id TEXT NOT NULL," +
                                    " shop_id INTEGER NOT NULL, count INTEGER NOT NULL, PRIMARY KEY(goods_barcode))"
                        )
//                        复制旧表中的数据到新表之中
                        database.execSQL(
                            "INSERT INTO new_stock_goods(goods_barcode, goods_name, goods_price, id, goods_id, shop_id, count) " +
                                    "SELECT goods_barcode, goods_name, goods_price, id, goods_id, shop_id, count FROM stockgoods"
                        )
//                        删除旧表
                        database.execSQL("DROP TABLE IF EXISTS stockgoods")
//                        修改新表的名称为旧表
                        database.execSQL("ALTER TABLE new_stock_goods RENAME TO stockgoods")

这是我参考网上博客得到的新方法,既然不能直接删除,那么就只能新建一个表了。

首先我们新建一个表,取名为new_stock_goods,然后定义各种字段,字段名称和旧表一致,只是旧表不要的goods_model我们就不要定义了。

然后我们将旧表中的数据复制到新表之中,goods_model的数据不需要复制。

复制完了之后我们就删除旧表,最后将新版的名字改成旧表的stock_goods ,这样下来就算完成了。

总的来说还是比较麻烦的,我现在表中的字段只有七八个,一个个写下来就有点麻烦了,要是再增加的话,更是一种折磨,不知道有没有更好的迁移方法,如果有大神知道,希望能告知一下。

另外说一下测试room数据迁移时遇到的坑:

我在上面创建新表的sql语句中,每一个字段后面都加了NOT NULL这句话,这是对数据库字段属性的声明。数据库每一个column都有notNull这个属性,在字段加了NOT NULL这句话之后,它对应的notNull就会等于true。如果我们不加的话,由于room通过@Entity注解默认创建的表中每一个字段的notNull都为true,因此如果在创建新表的字段后面不加NOT NULL,它的notNull就是false,那么就会出现java.lang.IllegalStateException: Migration didn't properly handle StockGoods的错误。

现在我将

database.execSQL(
                            "CREATE TABLE new_stock_goods (goods_barcode TEXT NOT NULL," +
                                    " goods_name TEXT NOT NULL, goods_price REAL NOT NULL, id INTEGER NOT NULL, goods_id TEXT NOT NULL," +
                                    " shop_id INTEGER NOT NULL, count INTEGER NOT NULL, PRIMARY KEY(goods_barcode))"
                        )

 中goods_barcode后面的NOT NULL去掉,然后再执行,就出现了错误

    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.hongyue.shopsystem/com.hongyue.shopsystem.ui.stock.activity.StockActivity}: java.lang.IllegalStateException: Migration didn't properly handle StockGoods(com.hongyue.shopsystem.mvp.model.bean.StockGoods).
     Expected:
    TableInfo{name='StockGoods', columns={goods_id=Column{name='goods_id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, goods_name=Column{name='goods_name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, count=Column{name='count', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, shop_id=Column{name='shop_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, goods_barcode=Column{name='goods_barcode', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, goods_price=Column{name='goods_price', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='StockGoods', columns={goods_id=Column{name='goods_id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, count=Column{name='count', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, goods_name=Column{name='goods_name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0}, shop_id=Column{name='shop_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, goods_barcode=Column{name='goods_barcode', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=1}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, goods_price=Column{name='goods_price', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=null}

该错误是由新表和@Entity注解的类对应的数据表格式不同导致的,对比上面的错误信息我们可以找到这么一点不同


     Expected:
    goods_barcode=Column{name='goods_barcode', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1}
     Found:
    goods_barcode=Column{name='goods_barcode', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=1}

其他的属性都一样,只有notNull不一样,这也是造成错误的原因,因此如果再出现java.lang.IllegalStateException: Migration didn't properly handle的错误,那么要仔细对比错误信息,看看到底是什么原因导致的,将对应的属性修改一样就可以了。

你可能感兴趣的:(Room的数据迁移踩坑)