iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 17
0
Mobile Development

Android Architecture Components 學習心得筆記系列 第 17

Day 17 Room (二) Query 的詳細用法以及如何升級(Migrate)資料庫版本

Room(二)

Query

Query 是 DAO 中使用的主要註解。它允許在資料庫上執行讀/寫操作。每個 @Query 方法都在編譯時被檢查,因此,如果存在查詢問題,則會在編譯時就發生錯誤而不是在 runtime 的時候才被發現,有助於提升 debug 的速度。

1.如果只有一些字串匹配,則發出警告。
2.如果沒有字串匹配,則會報錯。

  • 簡單查詢
    返回 entity 的列表。
    @Query("SELECT * FROM room_entity")
    fun getAll(): List<RoomEntity>
  • 透過傳遞參數查詢
    返回一個 entity。
    @Query("SELECT * FROM room_entity WHERE name LIKE :name")
    fun findByName(name: String): RoomEntity

有些查詢可能要求傳遞一個可變數量的參數,其中參數的確切數目直到運行時才知道。例如,希望從區域的子集檢查有關所有用戶的信息。

    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegionsSync(regions: List<String>): <List<User>>
  • 與 LiveData 整合
    使用時直接返回一個 LiveData 類型,當資料庫被更新時,Room 就會生成所有必要的代碼來更新 LiveData。
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
  • RxJava
    Room 還可以從查詢中返回 RXJava2 的 Publisher 和 Flowable 對象。
    若要使用此功能,則要將 implementation "androidx.room:room-rxjava2:2.2.4" 庫添加到 gradle 中。
    @Query("SELECT * from user where id = :id LIMIT 1")
    fun loadUserById(id: Int): Flowable<User>
  • Cursor
    如果有需要的話也能返回一個 Cursor 類型。
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    fun loadRawUsersOlderThan(minAge: Int): Cursor

資料庫的升級、添加欄位

當資料庫已經存在並且被建立時,在不丟失原本資料的前提下,想要添加欄位是很麻煩的一件事情,一不小心就會把舊資料丟失或者蓋掉,甚至是一開啟資料庫就閃退等等的情形。

Room 在這方面也做了優化,使得升級資料庫變的相當容易,直接來看範例:

在已經存過資料的 Entity 添加一個欄位 imageUrl

@Entity(tableName = TABLE_NAME)
class RoomEntity {

...
...
    var imageUrl = ""
}

修改 Database

@Database(entities = [(RoomEntity::class)], version = 2)
abstract class RoomDbHelper : RoomDatabase() {

    companion object {
        @Volatile private var instance: RoomDbHelper? = null
        private val LOCK = Any()

        operator fun invoke(context: Context)= instance ?: synchronized(LOCK){
            instance ?: buildDatabase(context).also { instance = it}
        }

        private fun buildDatabase(context: Context) = Room.databaseBuilder(context,
            RoomDbHelper::class.java, "item-list.db")
            .addMigrations(MIGRATION_1_TO_2)
            .build()


        val MIGRATION_1_TO_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {

                val tableName = TABLE_NAME
                database.execSQL(
                "ALTER TABLE $tableName ADD COLUMN imageUrl TEXT NOT NULL DEFAULT ''")
            }
        }
    }

    abstract fun getRoomDao(): RoomDao
}

關鍵在這段

        val MIGRATION_1_TO_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {

                val tableName = TABLE_NAME
                database.execSQL(
                "ALTER TABLE $tableName ADD COLUMN imageUrl TEXT NOT NULL DEFAULT ''")
        }

Room 提供了一個類別 Migration 來升級版本,裡面只需要傳入兩個 Int 來表示目前的版本號以及要升級到多少的版本號。

    public Migration(int startVersion, int endVersion) {
        this.startVersion = startVersion;
        this.endVersion = endVersion;
    }

(Java source code)

並在覆寫的 migrate 方法裡面把要升級的 Table Name、欄位名稱和欄位型態寫進去。

在 buildDatabase 時添加 addMigrations(MIGRATION_1_TO_2)

        private fun buildDatabase(context: Context) = Room.databaseBuilder(context,
            RoomDbHelper::class.java, "item-list.db")
            .addMigrations(MIGRATION_1_TO_2)
            .build()

最後記得在 Database 把版本號修改一下,就成功囉。

@Database(entities = [(RoomEntity::class)], version = 2)

有任何問題或講得不清楚的地方歡迎留言和我討論。

更歡迎留言糾正我任何說錯的地方!

下一篇:Room (Last) 其他應用與總結


上一篇
Day 16 Room (一) 介紹與基本使用
下一篇
Day 18 Room (Last) 其他應用與總結
系列文
Android Architecture Components 學習心得筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言