在 Google I/O 2019,我們分享了 Room 2.2 的最新進展。盡管當時已經支持了很多功能,如支持 Flow API,支持預填充數據庫,支持一對一及多對多數據庫關系,但是開發者們對 Room 有著更高的期望,我們也致力于此,在 2.2.0 - 2.4.0 版本中發布了很多開發者們期待的新功能!包括自動化遷移,關系查詢方法以及支持 Kotlin Symbol Processing (KSP) 等等。下面我們就來逐一介紹這些新功能!
自動化遷移
在談自動化遷移之前,先看看什么是數據庫遷移。假如您更改了數據庫 schema,就需要根據數據庫版本進行遷移,以防用戶設備內置數據庫中現有數據丟失。如果您使用 Room,那么在數據庫遷移過程中會進行檢查并驗證更新后的 schema,另外您也可以在 @Database 中設置 exportSchema,來導出 schema 信息。
對于 Room 2.4.0 版本之前的數據庫遷移,您需要實現 Migration 類,并在其中編寫大量復雜冗長的 SQL 語句,來處理不同版本之間的遷移。這種手動遷移的形式,非常容易引發各種錯誤。
現在 Room 支持了自動遷移,讓我們通過兩個示例來對比手動遷移和自動遷移:
修改表名
假設有一個包含兩個表的數據庫,表名分別是 Artist 和 Track,現在想要將表名 Track 改為 Song。
如果使用手動遷移,必須編寫和執行 SQL 語句才能更改,需要如下操作:
如果使用自動遷移,您只需要在定義數據庫時添加 @AutoMigration 配置,同時提供兩個版本數據庫導出的 schema。Auto Migration API 將為您生成并實現 migrate 函數,編寫并執行遷移所需的 SQL 語句。代碼如下:val MIGRATION_1_2: Migration = Migration(1, 2) {
fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE `Track` RENAME TO `Song`")
}
}
@Database(
version = MusicDatabase.LATEST_VERSION
entities = {Song.class, Artist.class}
autoMigrations = {
@AutoMigration (from = 1,to = 2)
}
exprotSchema = true
)
修改字段名
現在,演示一個更復雜的場景,假設我們要將 Artist 表中的 singerName 字段修改為 artistName。
雖然這看起來很簡單,但是由于 SQLite 并沒有提供用于此操作的 API,因此我們需要根據 ALERT TABLE 實現,有如下幾步操作:
1.獲取需要執行更改的表
2.創建一個新表,滿足更改后的表結構
3.將舊表的數據插入到新表中
4.刪除舊表
5.把新表重命名為原表名稱
6.進行外鍵檢查
遷移代碼如下:
val MIGRATION_1_2: Migration = Mirgation(1, 2) {
fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_Artist`(`id` INTEGER NOT
NULL, artistName` TEXT, PRIMARY KEY(`id`)"
)
db.execSQL("INSERT INTO `_new_Artist` (id,artistName)
SELECT id, singerName FROM `Artist`"
)
db.execSQL("DROP TABLE `Artist`")
db.execSQL("ALTER TABLE `_new_Artist` RENAME TO `Artist`")
db.execSQL("PRAGMA foreign_key_check(`Artist`)")
}
}
從上面的代碼就可以看出,如果使用手動遷移,即使兩個版本之間僅有一處更改,也可能需要繁瑣的操作,并且這些操作極易出錯。
那我們來看看自動遷移該如何使用。在上面的示例中,自動遷移無法直接處理重命名表中的某一列,因為 Room 在進行自動遷移時,會遍歷兩個版本的數據庫 schema,通過比較來檢測兩者之間的更改。在處理列或者表的重命名時,Room 無法明確發生了什么更改,此時可能有兩種情況,是刪除后新添加的?還是進行了重命名?處理列或者表的刪除操作時也會有同樣問題。
所以我們需要給 Room 添加一些配置來說明這些不確定的場景——定義 AutoMigrationSpec。AutoMigrationSpec 是定義自動遷移規范的接口,我們需要實現該類,并在實現類上添加和修改相對應的注解。本例中,我們使用 @RenameColumn 注解,并在注解參數中,提供表名、列的原始名稱以及更新后的名稱。如果在遷移完成之后,還需要執行其他任務,可以在 AutoMigrationSpec 的 onPostMigrate 函數中進行處理,相關代碼如下:
tableName = ,
fromColumnName = ,
toColumnName =
)
static class MySpec : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// 遷移工作完成后處理任務的回調
}
}
完成 AutoMigrationSpec 的實現后,還需要將其添加到數據庫定義時配置的 @AutoMigation 中,同時提供兩個版本的數據庫 schema,Auto Migration API 將生成和實現 migrate 函數,配置代碼如下:
@Database(
version = MusicDatabase.LATEST_VERSION
entities = {Song.class, Artist.class}
autoMigrations = {
@AutoMigration (from = 1,to = 2,spec = MySpec.class)
}
exprotSchema = true
)
上面的案例提到了 @RenameColumn,相關的變更處理注解有如下幾種:
- @DeleteColumn
- @DeleteTable
- @RenameColumn
- @RenameTable
假設在同一遷移中有多個更改需要配置,我們還可以通過這些可復用的注解簡化處理。
測試自動遷移
假設您在一開始就使用了自動遷移,現在希望測試其是否正常工作,可以使用現有的 MigrationTestHelper API 無需任何更改。如以下代碼:
fun v1ToV2() {
val helper = MigrationTestHelper(
InstrumentationRegisty.getInstrumentation(),
AutoMigrationDbKotlin::class.java
)
val db: SupportSQLiteDatabase = helper.runMigrationsAndValidate(
name = TEST_DB,
version = 2,
validateDroppedTables = true
)
}
在無需額外配置的情況下,MigrationTestHelper 將自動運行并驗證所有自動遷移。在 Room 內部,如果存在自動遷移,它們將自動添加到需要運行和驗證的遷移列表中。
需要注意的是,開發者提供的遷移具有更高的優先級,也就是說,如果您定義自動遷移的兩個版本之間,已經定義了手動遷移,那么手動遷移將優先于自動遷移。
關系查詢方法
關系查詢也是新增的一個重要功能,我們還是用一個示例說明。
假設我們使用與之前相同的數據庫和表,現在表名分別為 Artist 和 Song。如果我們希望獲得音樂人到歌曲的映射集合,就要在 artistName 和 songName 之間建立關系。如下圖中 Purple Lloyd 與其熱門歌曲《Another Tile in the Ceiling》和《The Great Pig in the Sky》匹配,AB/CD 將與其熱門歌曲《Back in White》和《Highway to Heaven》匹配。
使用 @Relation如果使用 @Relation 和 @Embedded 反應該映射關系,則有如下代碼:
data class ArtistAndSongs(
val artist: Artist,
val songs: List
)
fungetArtistsAndSongs():List
在此方案中,我們創建了全新的數據類,將音樂人和歌曲列表相關系。但是這種額外創建 data 類的方式,容易造成代碼繁冗的問題。而 @Relation 中并不支持過濾、排序、分組或組合鍵,其設計初衷也是用于數據庫中只有一些簡單的關系,雖然受限于關系結果,但這是一種快速完成較簡單任務的便捷方法。
所以為了支持復雜關系的處理,我們并沒有擴展 @Relation,而是希望您充分發揮 SQL 的潛能,因為它的功能非常強大。
接下來讓我們來看看 Room 如何利用全新的功能來解決這一問題。
使用全新關系查詢功能
為了表示前面所示的音樂人與其歌曲之間的關系,我們現在可以編寫一個簡單的 DAO 方法,其返回類型為 Map,而我們需要做的僅僅是提供 @Query 和返回標記,Room 將為您處理其余的一切!相關代碼如下:
fungetAllArtistAndTheirSongsList():Map>,>
在 Room 內部,實際上要做的是找到音樂人、歌曲和 Cursor 并將它們放入 Map 中的 Key 和 Value 中。
在本例中,涉及到一對多的映射關系,其中單個音樂人映射到一個歌曲集合。當然我們也可以使用一對一映射,如下文所示:
// 一對一映射關系
fungetSongAndArtist():Map,>
使用 @MapInfo
實際上,您可以通過 @MapInfo 在映射的使用中更加靈活。
MapInfo 是用于說明開發者配置的輔助程序 API,類似于前面談到的自動遷移更改注解。您可以使用 MapInfo 明確說明您希望如何處理查詢到的 Cursor 所包含的信息。使用 MapInfo 注解您可以指定輸出的數據結構中用于查詢的 Key 和 Value 所映射的列。需要注意,用于 Key 的類型必須實現 equals 和 hashCode 函數因為這對映射過程非常重要。
假設我們希望以 artistName 作為 Key,獲得歌曲列表作為 Value,則代碼實現如下:
在該示例中,artistName 用作 Key,音樂人被映射到其歌曲名稱列表,最后 artistName 被映射到其歌曲名稱列表。fungetArtistNameToSongs():Map>,>,>,>
MapInfo 注解使您可以靈活地使用特定列,而不是整個 data 類從而進行更加自定義的映射。
其他優勢
關系查詢方法的另一個好處是支持更多的數據操作,可以通過這個新功能來支持分組、篩選等功能。示例代碼如下:@MapInfo(valueColumn = "songCount")
@Query("
SELECT *, COUNT(songId) as songCount FROM Artist JOIN Song ON
Artist.artistName = Song.songArtistName
GROUP BY artistName WHERE songCount = 2
")
fungetArtistAndSongCountMap():Map,>
最后需要注意多重映射是一個核心返回類型,可以使用 Room 已經支持的各種可觀察類型封裝 (包括 LiveData、Flowable、Flow)。因此,關系查詢方法可讓您輕松地在數據庫中定義任意數量的關聯關系。
更多新功能
內置 Enum 類型轉換器
現在,如果系統未提供任何類型轉換器,Room 將默認使用 "枚舉 - 字符串" 雙向類型轉換器。如果已存在適用于枚舉的類型轉換器,Room 將優先使用該轉換器,而不使用默認轉換器。
支持查詢回調現在,Room 提供了一個通用 callback API RoomDatabase.QueryCallback,此 API 會在執行查詢時被調用,這將非常有助于我們在 Debug 模式下記錄日志??赏ㄟ^ RoomDatabase.Builder#setQueryCallback() 設置此回調。
如果您希望記錄查詢以了解數據庫中發生了什么,該功能可以幫助您進行記錄,示例代碼如下:fun setUp() {
database = databaseBuilder.setQueryCallback(
RoomDatabase.QueryCallback{ sqlQuery, bindArgs ->
// 記錄所有觸發的查詢
Log.d(TAG, "SQL Query $sqlQuery")
},
myBackgroundExecutor
).build()
}
支持原生 Paging 3.0 API
Room 現在支持為返回值類型為 androidx.paging.PagingSource 且帶 @Query 注解的方法生成實現。
支持 RxJava3
Room 現在支持 RxJava3 類型。通過依賴 androidx.room:room-rxjava3,您可以聲明返回值類型為 Flowable、Single、Maybe 和 Completable 的 DAO 方法。
支持 Kotlin Symbol Processing (KSP)
KSP 用于替代 KAPT,它能夠在 Kotlin 編譯器上以原生方式運行注解處理器,從而顯著縮短構建時間。
對于 Room,使用 KSP 有如下好處:
- 提高 2 倍的構建速度;
- 直接處理 Kotlin 代碼,更好的支持空安全。
隨著 KSP 的穩定,Room 將使用其功能實現 value 類、生成 Kotlin 代碼等。
從 KAPT 遷移到 KSP 非常簡單,只需使用 KSP 插件替換 KAPT 插件,并使用 KSP 配置 Room 注解處理器,示例代碼如下:
plugins{
// 使用 KSP 插件替換 KATP 插件
// id("kotlin-kapt")
id("com.google.devtools.ksp")
}
dependencies{
// 使用 KSP 配置替代 KAPT
// kapt "androidx.room$version"
ksp "androidx.room$version"
}
總結
自動化遷移、關系查詢方法、KSP——Room 帶來了很多新功能,希望大家和我們一樣對所有這些 Room 更新感到興奮,記得查看并開始在您的應用中使用這些新功能!
原文標題:深入探討 Room 2.4.0 的最新進展
文章出處:【微信公眾號:谷歌開發者】歡迎添加關注!文章轉載請注明出處。
審核編輯:湯梓紅
-
Google
+關注
關注
5文章
1789瀏覽量
59076 -
設備
+關注
關注
2文章
4668瀏覽量
71777 -
自動化
+關注
關注
29文章
5785瀏覽量
84975
原文標題:深入探討 Room 2.4.0 的最新進展
文章出處:【微信號:Google_Developers,微信公眾號:谷歌開發者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
專家開講:深入了解電池技術 ──Part 1
專家開講:深入了解電池技術──Part 3
單片機的深入了解!
Zigbee各版本對比,讓你深入了解
Hanlp1.7版本的新增功能一覽
示波器的深入了解

評論