如何使用Room的prepackagedDatabaseCallback解决schema校验与触发器创建问题
使用Room PrepackagedDatabaseCallback的常见问题解答
如何通过该接口解决「Invalid Schema expected ... found ...」报错
首先需要明确:该接口并不是跳过Room的schema校验逻辑,而是在校验执行前修改预打包数据库的schema,使其完全匹配Room的期望,从根源消除报错,使用步骤如下:
- 先确认Room期望的schema与你的预打包数据库的schema差异,可通过Room生成的
app\schemas\XXXAppDatabase\X.json文件查看官方要求的表结构、字段、索引、约束等信息 - 自定义类实现
PrepackagedDatabaseCallback接口,重写onOpenPrepackagedDatabase方法,在方法内通过传入的SupportSQLiteDatabase实例执行SQL调整预打包库的结构
示例代码:val prepackagedCallback = object : PrepackagedDatabaseCallback() { override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) { super.onOpenPrepackagedDatabase(db) // 示例1:添加Room期望但预打包库缺少的字段 db.execSQL("ALTER TABLE user ADD COLUMN avatar TEXT DEFAULT '' NOT NULL") // 示例2:修改字段名匹配Room要求 db.execSQL("ALTER TABLE user RENAME COLUMN usr_name TO user_name") // 示例3:添加缺失的索引 db.execSQL("CREATE INDEX IF NOT EXISTS index_user_phone ON user(phone)") } } - 在Room数据库构造时,加载预打包库的方法中传入上述回调实例即可
示例代码:Room.databaseBuilder(context, AppDatabase::class.java, "app_database.db") // 从assets加载预打包库时传入回调,从文件加载用createFromFile方法同样支持传回调 .createFromAsset("prepackaged_local_db.db", prepackagedCallback) .build()
是否可以通过该回调添加触发器
可以。该回调执行时预打包数据库已经复制到app的私有目录,你拿到的是可写的数据库实例,直接执行创建触发器的SQL即可,触发器会被写入数据库,后续Room打开后正常生效。
示例代码:
val prepackagedCallback = object : PrepackagedDatabaseCallback() { override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) { super.onOpenPrepackagedDatabase(db) // 创建用户信息更新时自动修改更新时间的触发器 db.execSQL(""" CREATE TRIGGER IF NOT EXISTS trigger_update_user_modify_time AFTER UPDATE ON user FOR EACH ROW BEGIN UPDATE user SET modify_time = datetime('now') WHERE user_id = OLD.user_id; END """.trimIndent()) } }
注意:预打包数据库仅会在App首次启动创建Room数据库时复制一次,后续App升级不会再次触发该回调,如果升级时需要新增触发器,需要将创建逻辑写进Room的Migration迁移逻辑中。
内容的提问来源于stack exchange,提问作者MikeT
相关产品推荐
相关产品推荐

