本篇介紹 Room 的一些進階用法
基本上 App 只需要做呈現後端傳來的資料,Database並不會做得太複雜
不過凡事都有例外,多點知識多點武器便可在此世界多存活一點
本篇介紹 TypeConverters, Embed, Relation
前言
看我的介紹前,建議先去看google document, 裡面會有正確的觀念,
這邊只寫我的看法,請大家多多指教
當Entity使用有點複雜的物件,該物件不為 SQL 可以支援的基本格式,這時我們就可以只用 TypeConverter 來作轉換
以 google 的例子來解釋,
當我們想要存取 Date 格式的物件,因 SQLite 不支援這物件,但後面的操作使用 Date 物件會比較方便(假設來說)。
所以我們需要轉換 Long 的形式給 DB,之後在讀取時再轉為Date格式。
這時應該人有會想到這應該是 VO(view object) 要做的事情啊?
不過我會想到這應該是資料層可以做的事情吧。
Room 提供的方式就是 TypeConverters
使用方式很簡單,以Date來說 建立一個DateConverter
class DateConverter { @TypeConverter fun fromTimestamp(value: Long?): Date? { return value?.let { Date(it) } } @TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time } }
Function name 可以自訂,但一定要有兩個function,
1. Date -> Long
2. Long -> Date
並在上面加上@TypeConverter
並在RoomDatabase中添加
@TypeConverters(DateConverter::class) abstract class AppDatabase : RoomDatabase() { ... }
enum class也可以用此方法處理
在實際案例中,我們時常碰到database這塊,無論新增資table或新增資料,往往都有重複性質的欄位
如: createAt, updateAt, delete_flag等等, 這些常用的欄位,是否可以抽出來通用呢?
這時我腦子想,就多打幾行就好了,每個table中的欄位都是特殊的不需要通用才對。我只能說看情況囉
看重複性質多高,看使用的table多寡,看是否有code潔癖需要這樣做
Embed annotation 就可以幫我們做到這些
使用情境:
1. 需要複製已有的table並加以調整。
2. 設計table時,發現有許多想同欄位,且不會有不同用途。如:createAt, updateAt 。
以上面情境可以整理成 當 Entity 與 Pojo 這種單純型式的class可以適用。
以下為我的範例
我們創造兩個table分別為, User, Pet。 他們中都含有一些基本資訊,如name, age, gender, createData 這四個資訊。
把這些資訊做成Info.kt
import java.util.* data class Info( var name: String? = null, var age: Int = 0, var gender: Gender? = null, var createDate: Date? = null )
Entity 則如下:
@Entity class User( @Embedded(prefix = "user_") var info: Info ) { @PrimaryKey(autoGenerate = true) var uid: Int = 0 }
@Entity class Pet( @Embedded(prefix = "pet_") var info: Info? ) { @PrimaryKey(autoGenerate = true) var pid = 0 var kind: String? = null var uid = 0 }
prefix 則為column name 的前啜字 (可加可不加)
DB 架構如下
當我們需要對多個table做交集(Join)時,以前的方式會用cursor處理,更原始的就直接把table load出來,再自行組回去。
而Room有提供這需求,讓我們簡單攥寫
首先我們先建立一個Pojo UserAndAllPets class
class UserAndAllPets { @Embedded var user: User? = null @Relation( parentColumn = "uid", entityColumn = "uid", entity = Pet::class) var pets: List<Pet?>? = null }
parentColumn 為 User 的 uid,
entityColumn 為 Pet 的 uid, for query的實體物件
這邊我開一個 UserPetDao class出來,不過為了防止類別爆炸也可以放入UserDao裡
@Dao interface UserPetDao { @Query("SELECT * FROM User ORDER BY user_createDate") fun loadUserAndPets(): LiveData<List<UserAndAllPets>> }
使用方式
AppDatabase.getInstance(application).userPetDao().loadUserAndPets().observe(this@MainActivity, androidx.lifecycle.Observer { list -> //TODO print log for debug ... }
記得把Log 印下來 資料是否正確
我把 ball 的uid 設定為apple, 而 beer 沒有任何寵物。其結果如下
15787-15787/com.owen.roomsample D/MainActivity: list size: 2 15787-15787/com.owen.roomsample D/MainActivity: user name: beer 15787-15787/com.owen.roomsample D/MainActivity: user name: apple 15787-15787/com.owen.roomsample D/MainActivity: pet: ball
專案範例,有興趣的可以看我的sample,看一步步的撰寫的過程
https://gitlab.com/jc7003/roomsample
參考資料
https://developer.android.com/reference/android/arch/persistence/room/TypeConverters
https://developer.android.com/reference/android/arch/persistence/room/Relation.html
https://developer.android.com/reference/android/arch/persistence/room/Embedded
相關文章
留言列表