问题的背景
随着应用开发的不断深入,或早或晚都会产生升级数据库结构的需求。这种升级可以是为特定的数据表添加字段,也可以是增加新表等。
以秒表应用为例,升级之前的表构成如下,这个数据库中一共包含3个表:
@Database(entities = {TimeRecord.class, TileData.class, Setting.class}, version = 1)
public abstract class StopWatchDB extends OrmDatabase {
}
假设我们需要增加第4个表TimingInfo。如果应用还没有发布,我们直接将这个表加进来,然后卸载应用(这时会清除应用数据)就行了。
@Database(entities = {TimeRecord.class, TileData.class, Setting.class, TimingInfo.class}, version = 1)
public abstract class StopWatchDB extends OrmDatabase {
}
但如果应用已经发布并被大量用户使用,清除已有数据重新建库的方法就不能用了。本文介绍在保留已有数据的情况下实现数据库升级的方法。
升级数据库版本
以下代码是升级数据库的第一步,增加新表和提升版本信息。
@Database(entities = {TimeRecord.class, TileData.class, Setting.class, TimingInfo.class}, version = 2)
public abstract class StopWatchDB extends OrmDatabase {
}
注意第一行代码的最后,version的值已经修改为2。需要注意的是,版本号使用的是整数值。
实现数据库升级类
如果只是修改数据库版本信息,在实际使用增加的新表时,会发生下面的异常:
信息是需要的表不存在。要解决这个问题,获取数据库上下文时,要指定版本之间进行迁移的处理类。:
private OrmContext getOrmContext(){
DatabaseHelper helper = new DatabaseHelper(this);
return helper.getOrmContext("StopWatch",
"StopWatch.db",
StopWatchDB.class,
new TestOrmMigration12());
}
private static class TestOrmMigration12 extends OrmMigration {
// 此处用于配置数据库版本迁移的开始版本和结束版本,super(startVersion, endVersion)即数据库版本号从1升到2。
public TestOrmMigration12() {super(1, 2); }
@Override
public void onMigrate(RdbStore rdbStore) {
//数据库升级处理
}
}
代码第6行将TestOrmMigration12登录到获取数据库上下文处理中,而代码第11行指定了这个类用于版本1升级到版本2的处理。
数据升级处理
本例中增加了一个新的TimingInfo表,我们可以自己手写增加表的语句,也可以参照开发工具自动生成的语句,这样即省时省力,又能保证正确性。对于StopWatch应用来说,我们可以从以下文件中找到对应处理:
StopWatch\entry\build\generated\source\annotation\debug\xwg\stopwatch\db\StopWatchDBImpl.java
@Override
public void onCreate(RdbStore store) {
store.executeSql("CREATE TABLE IF NOT EXISTS `setting` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `segment` TEXT , `item` TEXT , `value` TEXT )");
store.executeSql("CREATE UNIQUE INDEX `index_setting_index` ON `setting` (`segment`,`item`)");
store.executeSql("CREATE TABLE IF NOT EXISTS `TimingInfo` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `startTime` INTEGER , `title` TEXT )");
store.executeSql("CREATE UNIQUE INDEX `index_time_index` ON `TimingInfo` (`startTime`)");
store.executeSql("CREATE TABLE IF NOT EXISTS `time_record` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `recordId` INTEGER , `lapNumber` INTEGER , `time` INTEGER )");
store.executeSql("CREATE UNIQUE INDEX `index_record_index` ON `time_record` (`recordId`,`lapNumber`)");
store.executeSql("CREATE TABLE IF NOT EXISTS `tile_data` (`tileId` INTEGER PRIMARY KEY AUTOINCREMENT, `type` INTEGER , `zoom` INTEGER , `tileX` INTEGER , `tileY` INTEGER , `data` BLOB )");
store.executeSql("CREATE UNIQUE INDEX `index_tile_index` ON `tile_data` (`type`,`zoom`,`tileX`,`tileY`)");
}
代码第5,6两行就是我们想要的。将它拷贝粘贴到TestOrmMigration12的onMigrate方法中并稍加修改即可。
private static class TestOrmMigration12 extends OrmMigration {
// 此处用于配置数据库版本迁移的开始版本和结束版本,super(startVersion, endVersion)即数据库版本号从1升到2。
public TestOrmMigration12() {super(1, 2); }
@Override
public void onMigrate(RdbStore rdbStore) {
rdbStore.executeSql("CREATE TABLE IF NOT EXISTS `TimingInfo` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `startTime` INTEGER , `title` TEXT )");
rdbStore.executeSql("CREATE UNIQUE INDEX `index_time_index` ON `TimingInfo` (`startTime`)");
}
}
参考代码
https://github.com/xueweiguo/Harmony/tree/master/StopWatch
参考资料
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-orm-overview-0000000000030070
开发-对象关系映射数据库开发指导 (harmonyos.com)
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-orm-guidelines-0000000000030063
作者著作介绍
《实战Python设计模式》是作者去年3月份出版的技术书籍,该书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。
对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。