数据迁移
当你添加或删除 Collection、或其字段或索引时,Isar 会自动为你的数据库 Schema 做数据迁移。有时候你可能想要自行迁移。Isar 没有提供相关函数,因为这么做会给数据迁移强行加了限制。根据自己的需求来自由实现迁移功能其实很简单。
在下方的例子中,我们希望使用整个数据库的单一版本。我们用 shared_preferences 这个库来保存当下的版本,然后跟我们要迁移的版本做比较。如果两个版本不匹配,那么就迁移数据,更新版本。
提示
你也可以给每个 Collection 分配单独的版本,然后单独为它们各自做数据迁移。
假设我们有一个用户 Collection,它包含一个出生日 birthday 字段。在我们第二版 App 中,我们需要根据用户年龄来查询用户,就必须添加额外的出生年份字段。
版本 1:
@collection
class User {
late int id;
late String name;
late DateTime birthday;
}
版本 2:
@collection
class User {
late int id;
late String name;
late DateTime birthday;
short get birthYear => birthday.year;
}
可问题是现有的用户数据中不会有 birthYear
这个字段的信息,因为它在版本 1 中不存在。我们需要借用数据迁移来为 birthYear
字段赋值。
import 'package:isar/isar.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
final dir = await getApplicationDocumentsDirectory();
final isar = await Isar.openAsync(
schemas: [UserSchema],
directory: dir.path,
);
await performMigrationIfNeeded(isar);
runApp(MyApp(isar: isar));
}
Future<void> performMigrationIfNeeded(Isar isar) async {
final prefs = await SharedPreferences.getInstance();
final currentVersion = prefs.getInt('version') ?? 2;
switch(currentVersion) {
case 1:
await migrateV1ToV2(isar);
break;
case 2:
// 如果版本未设置(新建的时候)或已经是版本 2,我们就不做处理
return;
default:
throw Exception('Unknown version: $currentVersion');
}
// 更新版本
await prefs.setInt('version', 2);
}
Future<void> migrateV1ToV2(Isar isar) async {
final userCount = await isar.users.count();
// 我们对用户数据进行分页读写,避免同时将所有数据存放到内存
for (var i = 0; i < userCount; i += 50) {
final users = await isar.users.where().offset(i).limit(50).findAll();
await isar.writeTxn((isar) async {
// 我们不需要更新任何信息,因为 birthYear 的 getter 已经被使用
await isar.users.putAll(users);
});
}
}
警告
如果你需要迁移大量数据,考虑在后台使用另一个 isolate 来处理,以防止对 UI 进程造成阻塞。