簡介
Room persistence library(以下皆簡稱為Room)是架構在SQLite上的抽象層,將SQLite的語法包裝起來,讓開發者在使用上更加便利;相較於原生的SQLite,Room有以下幾項優點
- 編譯時對SQL語法做檢查,因此不會等到執行時才發現語法錯誤
- 透過註解的方式操作資料庫,降低需要大量編寫SQL語法的時間
- 在更新資料庫方面更加的流線化
根據上述這些優點,Google建議開發者在使用時優先選用Room,那麼就來看看這個套件怎麼使用吧!
使用方法
跟大多套件一樣,在開始使用前必須先加入依賴
dependencies {
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// 選用,支援Kotlin Extensions 和 Coroutines
implementation "androidx.room:room-ktx:$room_version"
}
接著介紹Room的主要元件
Room三大元件-Database、DAO、Entity
先看這張在Google開發者文件中的圖
這張圖解釋了Room套件在Android APP中如何運作:當需要讀取或更動資料庫內的資料時,APP先向Room Database要DAOs,接著透過不同的DAO去讀取或者更動Entities,最後再藉由Entity來得到每個欄位資料。
了解運作流程後,接著就是實作的部分;雖然前面的流程是Database→DAO→Entity,但因為上層的元件會需要用到下層元件,因此為了更清楚了解元件之間的關係,實作會反過來從Entity開始,再介紹DAO,最後才是Database。
Entity
Entity是Room套件裡面表示資料庫儲存內容的元件,每個Entity的實體都可以看作是一筆儲存的資料,也因此如果是用Kotlin開發,通常會選用Data Class當作註解為Entity的載體。
假設要定義一個User資料表,最基本的只要定義User這個Data Class以及裡面各項參數,接著加上註解@Entity就完成了;不過可以看到上圖中除了@Entity以外還有其他註解以及註解內的屬性,這是Room為了讓使用上更方便所設計的。
當註解某個類別為Entity時,Room會自動將該類別名稱作為Table名稱,如果想要自訂名稱,則可以在@Entity註解中加入屬性(tableName=”表格名稱”),這樣建立出來的表格名稱就會是輸入的名稱。
而每個Entity中一定要有Primary Key(以下簡稱PK),來當作每筆資料的識別欄位,如果只要用單一欄位作為PK,則在要做為PK的欄位前加上註解@PrimaryKey,另外可以透過autoGenerate=true來讓欄位自動生成資料。
跟表格名稱一樣,Room也會自動將Data Class中的每個屬性名稱作為欄位名稱,因此如果要另外命名欄位,則可以在屬性前加上註解@ColumnInfo並加入屬性(name=”欄位名稱”);另外,如果想要讓該欄位有預設值,可以透過@ColumnInfo中的屬性(defaultValue="預設值")來設定。
最後,可能有些資料在使用APP時會用到,但並不想存到資料庫裏面的,則可以藉由註解@Ignore,這樣這個欄位的資料就不會保存。(註:在Kotlin的使用上仍然有些問題,詳細情況可以參考這裡,在文章中就不多做介紹)
多欄位組成Primary Key
有些時候可能會希望將多個欄位共同當作PK,用來確保每筆資料的獨特性,比如一本書的書名和作者不太可能兩個都一樣,這時候可以透過設定@Entity註解中的primaryKeys屬性達成。
在子Entity中忽略父Entity欄位
在開發時通常會希望將多個類別中重複的欄位抽取出來作為父類別,之後這些類別再去繼承該父類別,這在Entity中也是可以辦到的;但如果想在子Entity中忽略某些父類別中的欄位,就沒辦法透過前面加上@Ignore註解的方式來達成,而是改由透過@Entity註解中的ignoredColumns屬性來忽略想要的欄位。
DAO-Data Access Object
在使用Room時,如果需要讀取資料或者對資料進行更動(也就是所謂的CRUD),需要透過DAO來操作,而雖然DAO可以是interface或者abstract class,但Google建議在大多數情況下還是使用interface,因此下面會用interface來作為例子。
為了管理上的方便以及程式碼架構的整潔,一個DAO只會對應到一個Entity;與Entity相同,要使用DAO一樣在建立完該interface後加上@Dao註解就好,但不同的是在DAO中沒有任何的屬性,而是有各式各樣的方法來讓APP能夠對該資料表進行操作。
而每個方法會透過不同的註解來表示這個方法會對資料表進行怎樣的操作,這邊可以分為兩類:便利方法(Convenience methods,包括@Insert、@Update、@Delete)和@Query;前面有提到,Room是建立在SQLite上的套件,因此底層部分還是需要透過SQLite來運作,因此對資料庫的處理其實還是需要透過各種query來完成,而便利方法則是將一些基本的query包裝起來,因此開發者不需要寫query就可以完成操作,而如果是要做比較複雜的操作,則可以透過@Query寫出本來SQLite的query來達成;關於DAO的註解還有很多值得注意的細節,之後會再開一篇來仔細講解這部分。
Database
前面定義完了資料庫的儲存實體(Entity)以及操作方法(DAO)後,最後就是要將它們整合起來,並建立一個資料庫了;關於Room資料庫的建立有幾點要注意
- Database類別需要是抽象類別,同時繼承RoomDatabase這個類別
- 記得在前面加上@Database註解,同時在註解的entities屬性中,傳入要建立資料表的Entity類別,例如上圖中的User類別
- 每一個建立在資料庫中的DAO,再Database類別中都需要有一個對應的無參數抽象方法,並回傳該DAO的實體
- 資料庫實體的產生很耗資源,而且在使用上並不需要多個資料庫,因此多會透過Singleton的方式來回傳資料庫
根據以上幾點可以寫出Database類別如下
實際使用
到目前已經把三個主要的原件都建立好了,接下來就是實際使用了;首先在需要使用到資料庫的context中取得資料庫實體,並透過這個資料庫實體來得到對應資料表的DAO
接下來就可以透過這個DAO來對目標資料表進行操作囉!但要注意的是Room是不允許DAO在主線程做操作,建議透過Coroutine來解決這個問題
透過GlobalScope,操作DAO插入一些新的User之後把它列出來,結果如下
小結
本來以為可以一篇寫完,結果發現DAO的操作其實還蠻多東西可以講的...所以就還是分篇囉😂
在這篇中簡單介紹了Room的基本元件,以及比較基礎的使用方法,如果有問題或者講錯的地方,歡迎留言告知。
參考資料
Save data in a local database using Room (Android developer doc)
Room介紹與基本使用