不同于目前存在的大多數資料庫,Room
不支持 Entitiy 對象間的直接引用。具體原因可以看這
內容大致上是說,如果要將 Entitiy 對象內引用的其他對象使用延遲加載 (lateinit) 的話,因為一般的 UI Thread 大約會花 16毫秒 來計算和繪製 layout,所以即使查詢時間極短(大約 5毫秒 ),都會消耗掉寶貴的 UI Thread 的時間,若不使用懶加載,又會消耗掉不該佔用的資源,所以 Room
不推薦 Entitiy 對象間的直接引用。
既然不能直接引用對象,Room 提供了一個關鍵字 ForeignKey
用來定義對象之間的引用關係。
例如:
有一個 Entity 對象 Book
,在 @Entity
註解內使用 ForeignKey
來定義和另一個 Entity 對象 User 的關係。
@Entity(foreignKeys = arrayOf(ForeignKey(
entity = User::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("user_id"))
)
)
data class Book(
@PrimaryKey val bookId: Int,
val title: String
)
ForeignKey
非常強大,他能夠知道這些引用的 Entity 哪時候被更新,並且執行想要的動作。
例如:在 ForeignKey
註解內使用 onDelete = CASCADE
來告訴資料庫,如果 User 被刪除了,那麼連同他底下的所有實體 Book
也一併刪除。
有時候,你想在實體中引用其他的對象,並且把該對象的成員作為自己的欄位,這時可以使用 @Embedded
來註解這個引用的對象。
例如:
data class Address(
val street: String?,
val state: String?,
val city: String?,
@ColumnInfo(name = "post_code") val postCode: Int
)
@Entity
data class User(
@PrimaryKey val id: Int,
val firstName: String?,
@Embedded val address: Address?
)
這是一個 User
的 Entity 對象,並用 @Embedded
註解了另一個物件 Address
,
包含進去的物件就會自動生成欄位,所以這是一個包含 PrimaryKey id
、firstName
、street
、state
、city
和 postCode
的 User
Entity 對象。
Room 也提供了便利的方法來完成基本型態和物件型態的轉換,只需要透過 @TypeConverter
註解即可完成。
例如:
把 Date
這個物件轉換成 Long,或是把 Long 轉換成 Date
class Converters {
@TypeConverter
fun timeStampToLong(timeStamp: Long?): Date? {
return timeStamp?.let { Date(it) }
}
@TypeConverter
fun dateToTimeStamp(date: Date?): Long? {
return date?.time?.toLong()
}
}
接著,在 Database 加上 @TypeConverters
註解,就可以使用在 Converters
這個類別裡面的 @TypeConverter
。
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
官方推薦使用 JUnit
來為資料庫寫測試,因為他不需要創建 Activity,能夠更快速地執行想要的測試。
以下是官方範例:
@RunWith(AndroidJUnit4::class)
class SimpleEntityReadWriteTest {
private lateinit var userDao: UserDao
private lateinit var db: TestDatabase
@Before
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(
context, TestDatabase::class.java).build()
userDao = db.getUserDao()
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun writeUserAndReadInList() {
val user: User = TestUtil.createUser(3).apply {
setName("george")
}
userDao.insert(user)
val byName = userDao.findUsersByName("george")
assertThat(byName.get(0), equalTo(user))
}
}
值得注意的一點,並不要為了測試新開另一個 Table,而是應該使用 inMemoryDatabaseBuilder
來獲取已經存在的資料庫,才能保持這個測試的封閉性。 官方文檔
以前在寫資料庫時都會寫一堆落落長的 SQL 語法,非常麻煩而且容易出錯,有時候不小心多打一個空格就要找老半天,(親身經歷)
而 Room 使用註解的方式把代碼縮到最簡,而且還能在編譯時幫忙尋找語法的錯誤,大幅提升了開發速度和除錯時間,可讀性和維護性也有顯著的提升,真是開發人員的一大福音。
有任何問題或講得不清楚的地方歡迎留言和我討論。
更歡迎留言糾正我任何說錯的地方!
下一篇:Paging (ㄧ) 介紹