個人原創(chuàng)酸茴,轉(zhuǎn)載請注明出處:http://www.reibang.com/p/ff4fbbda2cd6
概述
DBFlow作為一款結(jié)合了簡易性與高效率的開源數(shù)據(jù)庫框架艰争,深受不少android開發(fā)者的喜愛挤渔。我之前的一個app也采用了這款框架嗅剖。最近app更新猿规,需要升級數(shù)據(jù)庫艾猜。在不需要改變表的結(jié)構(gòu)的情況下衬鱼,DBFlow的升級操作十分簡單业筏,只需要改一下版本號就行了∧倨簦可不巧我這次的更新偏偏有涉及到對表的結(jié)構(gòu)的修改驾孔,這樣一來就要用到DBFlow的Migration功能芍秆,查閱了一下官方文檔,發(fā)現(xiàn)有除了對數(shù)據(jù)本身的操作外翠勉,支持的修改數(shù)據(jù)庫結(jié)構(gòu)操作很有限(只支持對table的重命名和新增column)妖啥,一些細節(jié)也講的不是很清楚,以下都是我個人的幾個實測(踩坑)对碌。
Migration的觸發(fā)時機與執(zhí)行順序
DBFlow支持同時定義多個Migration荆虱,所有的Migration的觸發(fā)時機都是在第一次對數(shù)據(jù)庫進行操作時觸發(fā)的(而不是Application或是MainActivity創(chuàng)建時)。至于多個Migration的執(zhí)行順序朽们,先上代碼:
@Database(version = 4)
public class AppDatabase {
@Migration(version = 2, database = AppDatabase.class, priority = 0)
public static class Migration1 extends BaseMigration {
@Override
public void migrate(DatabaseWrapper database) {
// run some code here
SQLite.update(Employee.class)
.set(Employee_Table.status.eq("Invalid"))
.where(Employee_Table.job.eq("Laid Off"))
.execute(database); // required inside a migration to pass the wrapper
}
}
@Migration(version = 4, database = AppDatabase.class, priority = 0)
public static class Migration2 extends BaseMigration {
@Override
public void migrate(DatabaseWrapper database) {
// run some code here
...
}
}
}
- 數(shù)據(jù)庫的版本會由低到高逐步升級怀读,即假設(shè)之前的數(shù)據(jù)庫版本為1,要升級的版本為4骑脱,那么該段代碼會先執(zhí)行Migration1將版本升級到2菜枷,再執(zhí)行Migration2將版本升級到4。
- 只有@Migration里version高于原數(shù)據(jù)庫版本的Migration會執(zhí)行叁丧,即如果之前的版本為3啤誊,那么該段代碼只會執(zhí)行Migration2。
- 在version相同時拥娄,由priority決定執(zhí)行順序蚊锹,priority高的先執(zhí)行,如果version和priority都相同稚瘾,則順序不確定(盡量避免)牡昆,取決于哪個class先被找到。
- Migration只對已有的舊版本數(shù)據(jù)庫有效摊欠,新創(chuàng)建的數(shù)據(jù)庫不會執(zhí)行任何version > 0的Migration丢烘。
version = 0的Migration
DBFlow專門設(shè)計了version=0的Migration,用于數(shù)據(jù)庫的初始化凄硼。如果定義了這種Migration铅协,則每次數(shù)據(jù)庫創(chuàng)建時都會自動調(diào)用該Migration,并且之后版本號為最新摊沉,不會在調(diào)用其他version的Migration狐史。例如以下代碼:
@Database(version = 3)
public class AppDatabase {
@Migration(version = 0, database = AppDatabase.class)
public static class Migration0 extends BaseMigration {
@Override
public void migrate(DatabaseWrapper database) {
// run some code here
SQLite.update(Employee.class)
.set(Employee_Table.status.eq("Invalid"))
.where(Employee_Table.job.eq("Laid Off"))
.execute(database); // required inside a migration to pass the wrapper
}
}
@Migration(version = 3, database = AppDatabase.class)
public static class Migration1 extends BaseMigration {
@Override
public void migrate(DatabaseWrapper database) {
// run some code here
...
}
}
}
如果是第一次創(chuàng)建數(shù)據(jù)庫,那么只會執(zhí)行Migration0说墨,反之如果之前有舊版數(shù)據(jù)庫(version一般大于0)骏全,那么只會執(zhí)行Migration1。
Migration里的Transaction
利用DBFlow的Transaction來進行數(shù)據(jù)庫的批量操作非常簡單尼斧,尤其是速度最快的FastStoreModelTransaction姜贡,非常適合用來進行數(shù)據(jù)庫創(chuàng)建和升級時的初始化錄入數(shù)據(jù)。如
FastStoreModelTransaction transaction = FastStoreModelTransaction
.saveBuilder(FlowManager.getModelAdapter(Knife.class))
.addAll(allKnives).build();
FlowManager.getDatabase(AppDatabase.class).executeTransaction(transaction);
不過棺棵,如果用version = 0的Migration來初始化就不能這么寫楼咳,會報錯熄捍,屬于循環(huán)調(diào)用數(shù)據(jù)庫。正確的寫法是:
@Database(version = 3)
public class AppDatabase {
@Migration(version = 0, database = AppDatabase.class)
public static class Migration0 extends BaseMigration {
@Override
public void migrate(DatabaseWrapper database) {
FastStoreModelTransaction transaction = FastStoreModelTransaction
.insertBuilder(FlowManager.getModelAdapter(Knife.class))
.addAll(allKnives).build();
transaction.execute(database);
}
}
}
對數(shù)據(jù)庫的其他結(jié)構(gòu)性操作
這就需要SQL的知識了母怜,基本都是通過database.execSQL(SQLExpression)來進行余耽。比如刪除column,需要用execSQL先創(chuàng)建一張臨時表存原數(shù)據(jù)苹熏,然后刪除原表碟贾,再新建一張與原表名稱相同的不包含要刪的column的新表,最后再將臨時表的數(shù)據(jù)錄入新表中轨域。很麻煩是吧袱耽?這也是DBFlow目前為止不是很人性化的一點問題(相比之下Litepal在這點上就做的很好,雖然性能上略差一些)干发。