Android Room 之儲存 Objects 中的 List

13kmsteady發表於2019-03-06

前言

Room 是官方推出的一個在 SQLite 上提供抽象層的持久儲存庫,提供了強大而可靠的 SQL 物件對映能力,並且支援 LiveDataRxJava

在專案中新增以下依賴

    def room_version = "2.1.0-alpha04"
    def lifecycle_version = "2.0.0"
    def rxjava_version = '2.1.7'
    def rxandroid_version = '2.1.0'
    // Room
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"
    // RxJava
    implementation "io.reactivex.rxjava2:rxjava:$rxjava_version"
    implementation "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation 'com.google.code.gson:gson:2.8.5'

    // 檢視資料庫
    implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
複製程式碼

當非同步查詢資料的時候,返回的物件可以是一個 LiveData 或者 Flowable 。如:

    @Query("SELECT * FROM user")
    abstract fun getAllUsers(): Flowable<List<User>>

    @Query("SELECT * FROM user")
    abstract fun getAllUser(): LiveData<List<User>>
複製程式碼

當我們儲存的實體類中包含 List ,如果按照普通的方式去定義 Entity。編譯的時候就會報以下錯誤:

Cannot figure out how to save this field into database. You can consider adding a type converter for it.
複製程式碼

這是因為 Room 無法直接儲存 List 型別的資料,接下來我們將解決這個問題。

實踐

  • 假設一個 User 物件會有很多本喜歡的書籍,一般定義的 Entity 類是以下格式:

    
    @Entity(tableName = "user")
    data class User(
    
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "id")
        val id: Int,
    
        @ColumnInfo(name = "name")
        val name: String,
    
        @ColumnInfo(name = "books")
        val books: List<Book>
    
    )
    
    data class Book(
        val bookName: String
    )
    複製程式碼
  • 目前的情況下,編譯專案就會遇到前面提到的錯誤。我們可以藉助 @TypeConverter 去轉換任意物件。例如定義一個 BookConverters

    class BookConverters {
    
        @TypeConverter
        fun stringToObject(value: String): List<Book> {
            val listType = object : TypeToken<List<Book>>() {
    
            }.type
            return Gson().fromJson(value, listType)
        }
    
        @TypeConverter
        fun objectToString(list: List<Book>): String {
            val gson = Gson()
            return gson.toJson(list)
        }
    }
    複製程式碼
  • 在實體類中新增 @TypeConverters 註解

    @Entity(tableName = "user")
    @TypeConverters(BookConverters::class)
    data class User(
    
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "id")
        val id: Int,
    
        @ColumnInfo(name = "name")
        val name: String,
    
        @ColumnInfo(name = "books")
        val books: List<Book>
    
    )
    
    data class Book(
        val bookName: String
    )
    
    複製程式碼

@TypeConverters

這個註解的作用,就是告訴 Room 可以使用哪些額外的型別轉換器。

一般定義的轉換器類格式都是固定的:

class AnyConverters {

    @TypeConverter
    fun stringToObject(value: String): List<Any> {
        val listType = object : TypeToken<List<Any>>() {

        }.type
        return Gson().fromJson(value, listType)
    }

    @TypeConverter
    fun objectToString(list: List<Any>): String {
        val gson = Gson()
        return gson.toJson(list)
    }
}
複製程式碼

使用 Facebook 推出的 Stetho 可以很方便的檢視資料庫中內容:

資料庫中的資料

程式碼示例

參考資料

stackoverflow
官方文件

相關文章