個(gè)人原創(chuàng),轉(zhuǎn)載請注明出處:http://www.itdecent.cn/p/ff4fbbda2cd6
概述
DBFlow作為一款結(jié)合了簡易性與高效率的開源數(shù)據(jù)庫框架,深受不少android開發(fā)者的喜愛。我之前的一個(gè)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),一些細(xì)節(jié)也講的不是很清楚,以下都是我個(gè)人的幾個(gè)實(shí)測(踩坑)。
Migration的觸發(fā)時(shí)機(jī)與執(zhí)行順序
DBFlow支持同時(shí)定義多個(gè)Migration,所有的Migration的觸發(fā)時(shí)機(jī)都是在第一次對數(shù)據(jù)庫進(jìn)行操作時(shí)觸發(fā)的(而不是Application或是MainActivity創(chuàng)建時(shí))。至于多個(gè)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相同時(shí),由priority決定執(zhí)行順序,priority高的先執(zhí)行,如果version和priority都相同,則順序不確定(盡量避免),取決于哪個(gè)class先被找到。
- Migration只對已有的舊版本數(shù)據(jù)庫有效,新創(chuàng)建的數(shù)據(jù)庫不會執(zhí)行任何version > 0的Migration。
version = 0的Migration
DBFlow專門設(shè)計(jì)了version=0的Migration,用于數(shù)據(jù)庫的初始化。如果定義了這種Migration,則每次數(shù)據(jù)庫創(chuàng)建時(shí)都會自動調(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來進(jìn)行數(shù)據(jù)庫的批量操作非常簡單,尤其是速度最快的FastStoreModelTransaction,非常適合用來進(jìn)行數(shù)據(jù)庫創(chuàng)建和升級時(shí)的初始化錄入數(shù)據(jù)。如
FastStoreModelTransaction transaction = FastStoreModelTransaction
.saveBuilder(FlowManager.getModelAdapter(Knife.class))
.addAll(allKnives).build();
FlowManager.getDatabase(AppDatabase.class).executeTransaction(transaction);
不過,如果用version = 0的Migration來初始化就不能這么寫,會報(bào)錯(cuò),屬于循環(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)來進(jìn)行。比如刪除column,需要用execSQL先創(chuàng)建一張臨時(shí)表存原數(shù)據(jù),然后刪除原表,再新建一張與原表名稱相同的不包含要刪的column的新表,最后再將臨時(shí)表的數(shù)據(jù)錄入新表中。很麻煩是吧?這也是DBFlow目前為止不是很人性化的一點(diǎn)問題(相比之下Litepal在這點(diǎn)上就做的很好,雖然性能上略差一些)。