一、簡(jiǎn)介
Room有三個(gè)主要的組件:Database、Dao、Entity
數(shù)據(jù)庫(kù)(Database):你可以使用該組件創(chuàng)建數(shù)據(jù)庫(kù)的持有者。該注解定義了實(shí)體列表,該類(lèi)的內(nèi)容定義了數(shù)據(jù)庫(kù)中的DAO列表。這也是訪問(wèn)底層連接的主要入口點(diǎn)。注解類(lèi)應(yīng)該是抽象的并且擴(kuò)展自
RoomDatabase。在運(yùn)行時(shí),你可以通過(guò)調(diào)用Room.databaseBuilder()(理解為常規(guī)數(shù)據(jù)庫(kù)的定義)或者Room.inMemoryDatabaseBuilder()(數(shù)據(jù)保存在內(nèi)存中,即程序關(guān)掉,數(shù)據(jù)丟失)獲取實(shí)例。實(shí)體(Entity):這個(gè)組件代表了持有數(shù)據(jù)庫(kù)表記錄的類(lèi)。對(duì)每種實(shí)體來(lái)說(shuō),創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù)表來(lái)持有所有項(xiàng)。你必須通過(guò)
Database中的entities數(shù)組來(lái)引用實(shí)體類(lèi)。實(shí)體的每個(gè)成員變量都被持久化在數(shù)據(jù)庫(kù)中,除非你注解其為@Ignore。數(shù)據(jù)訪問(wèn)對(duì)象(DAO):這個(gè)組件代表了作為DAO的類(lèi)或者接口。DAO是Room的主要組件,負(fù)責(zé)定義訪問(wèn)數(shù)據(jù)庫(kù)的方法。被注解
@Database的類(lèi)必須包含一個(gè)無(wú)參數(shù)的抽象方法并返回被@Dao注解的類(lèi)型。當(dāng)編譯時(shí)生成代碼時(shí),Room會(huì)創(chuàng)建該類(lèi)的實(shí)現(xiàn)。
Database、Dao、Entities 和 app 的關(guān)系圖如下所示:

二、如何使用
我們以簡(jiǎn)單示例來(lái)具體實(shí)現(xiàn)如上核心類(lèi):
1.添加依賴
添加相關(guān)room依賴包Room版本說(shuō)明,讀者可根據(jù)如上鏈接,獲取最新版本及按需依賴即可
2.定義Database類(lèi)
/**
* des:Database
* author:onexzgj
*/
@Database(entities = [Cheese::class], version = 1) //注釋1
abstract class CheeseDb : RoomDatabase() {
abstract fun cheeseDao(): CheeseDao
companion object {
private var instance: CheeseDb? = null
fun get(context: Context): CheeseDb {
if (instance == null) {
//注釋2
instance = Room.databaseBuilder(context, CheeseDb::class.java, "onexzgj")
//是否允許在主線程進(jìn)行查詢
.allowMainThreadQueries()
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
}
})
.build()
}
return instance!!
}
}
- 在注釋1處
使用@Database注解,表示是數(shù)據(jù)庫(kù)的入口類(lèi),參數(shù)entities是數(shù)組類(lèi)型,需要將當(dāng)前APP中的所有涉及到的數(shù)據(jù)庫(kù)表類(lèi),都寫(xiě)入這個(gè)參數(shù),version參數(shù)表示當(dāng)前數(shù)據(jù)庫(kù)的版本,其實(shí)還有一個(gè)參數(shù)為exportSchema =true,默認(rèn)為true,按照字面意思為導(dǎo)出數(shù)據(jù)庫(kù)概要,即每次操作數(shù)據(jù)庫(kù)的一個(gè)摘要信息,但是需要注意的事,如果手動(dòng)設(shè)置為true時(shí),需要在build.gradle中的指定導(dǎo)出文件的位置,如下所示:
defaultConfig {
...
javaCompileOptions{
annotationProcessorOptions{
arguments=["room.schemaLocation":"$projectDir/schemas".toString()]
}
}
}
這個(gè)時(shí)候,等程序運(yùn)行之后,就會(huì)在根目錄生成schemas目錄,并且產(chǎn)生一個(gè)json文件,如下所示:

- 在注釋2處
可以根據(jù)項(xiàng)目需要選擇內(nèi)存數(shù)據(jù)庫(kù),且還有如下回調(diào)方法,如下所示:
//創(chuàng)建一個(gè)內(nèi)存數(shù)據(jù)庫(kù)
//但是這種數(shù)據(jù)庫(kù)的數(shù)據(jù)只存在于內(nèi)存中,也就是進(jìn)程被殺之后,數(shù)據(jù)隨之丟失
Room.inMemoryDatabaseBuilder(...)
//是否允許在主線程進(jìn)行查詢
.allowMainThreadQueries()
//數(shù)據(jù)庫(kù)創(chuàng)建和打開(kāi)后的回調(diào)
.addCallback()
//設(shè)置查詢的線程池,一般不需要設(shè)置
.setQueryExecutor()
.openHelperFactory()
//room的日志模式
.setJournalMode()
//數(shù)據(jù)庫(kù)升級(jí)異常之后的回滾,默認(rèn)重新進(jìn)行創(chuàng)建
.fallbackToDestructiveMigration()
//數(shù)據(jù)庫(kù)升級(jí)異常后根據(jù)指定版本進(jìn)行回滾
.fallbackToDestructiveMigrationFrom()
// 數(shù)據(jù)庫(kù)遷移升級(jí)時(shí)使用,后文會(huì)提到
.addMigrations(CacheDatabase.sMigration)
3.定義Dao
@Dao
interface CheeseDao {
@Query("select * from cheese order by name ")
fun findAllCheese(): DataSource.Factory<Int, Cheese> //注釋1
@Query("select * from cheese order by name ")
fun getAllCheese(): List<Cheese>? //注釋2
@Insert
fun insert(cheeses: List<Cheese>)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(cheese: Cheese)
@Delete
fun delete(cheese: Cheese)
}
注釋1處
返回的數(shù)據(jù)類(lèi)型為DataSource.Factory<Int,Cheese>,注釋2處返回的數(shù)據(jù)類(lèi)型為L(zhǎng)ist<Cheese> 從這里可以看到Room數(shù)據(jù)庫(kù)是原生支持Paging框架,如果不了解Paging的可以查看筆者之前的文章Paging框架注釋2處
返回的數(shù)據(jù)類(lèi)型為 List<Cheese>? ,則可以直接返回List集合
當(dāng)然這里也可以搭配Rxjava2使用,返回對(duì)象為 Flowable<List<CheeseAndUser>> ,后續(xù)再介紹
4、定義Entity
@Entity
data class Cheese(
@PrimaryKey(autoGenerate = true) val id: Int, val name: String
)
通過(guò)@Entity注解標(biāo)注的類(lèi),則表示這個(gè)類(lèi)會(huì)映射到數(shù)據(jù)庫(kù)中,默認(rèn)表名為類(lèi)名,
通過(guò)@ PrimaryKey,標(biāo)明主鍵,通過(guò)設(shè)置autoGenerate參數(shù)設(shè)置true,則表示主鍵會(huì)自增長(zhǎng)
5、代碼中使用
val allCheese = CheeseDb.get(this).cheeseDao().getAllCheese()
Log.d("DATATA", allCheese?.get(0)?.name + ":" + allCheese?.size)
運(yùn)行結(jié)果如下所示:

到這里相信對(duì)Room數(shù)據(jù)庫(kù)有一個(gè)基本的了解,如果了解Orm或者GreenDao的同學(xué),應(yīng)該對(duì)上面的步驟或者創(chuàng)建不難理解,接下來(lái)介紹核心類(lèi)的具體參數(shù)的含義及用法
三、核心類(lèi)屬性講解
3.1 、@Entity注解包含的屬性有:
tableName:設(shè)置表名字,默認(rèn)是類(lèi)的名字。
indices:設(shè)置索引,按需添加,會(huì)提高查詢速度,增加更新和新增的操作時(shí)間
inheritSuperIndices:父類(lèi)的索引是否會(huì)自動(dòng)被當(dāng)前類(lèi)繼承,沒(méi)用到過(guò),暫不解釋
primaryKeys:設(shè)置主鍵,一般通過(guò)直接在主鍵字段上添加@PrimaryKey
foreignKeys:設(shè)置外鍵。
Ignore:設(shè)置不需要映射到數(shù)據(jù)庫(kù)中的字段可以使用,則不會(huì)在表中出現(xiàn)該字段
如下所示:
@Entity(tableName = "table_cheese")
data class Cheese(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "testName")
val name: String,
@Ignore
val temp:String
)
3.2、@Query注解是
它是DAO類(lèi)中使用的主要注釋?zhuān)试S對(duì)數(shù)據(jù)庫(kù)執(zhí)行讀/寫(xiě)操作。@Query在編譯的時(shí)候會(huì)驗(yàn)證準(zhǔn)確性,所以如果查詢出現(xiàn)問(wèn)題在編譯的時(shí)候就會(huì)報(bào)錯(cuò),如字段拼寫(xiě)常規(guī)錯(cuò)誤等。
Room還會(huì)驗(yàn)證查詢的返回值,如果返回對(duì)象中的字段名稱(chēng)與查詢響應(yīng)中的相應(yīng)列名稱(chēng)不匹配的時(shí)候,Room會(huì)通過(guò)以下兩種方式之一提醒您:
如果只有一些字段名稱(chēng)匹配,它會(huì)發(fā)出警告。
如果沒(méi)有字段名稱(chēng)匹配,它會(huì)發(fā)生錯(cuò)誤。
@Query注解value參數(shù):查詢語(yǔ)句,根據(jù)需求,完成查詢sql語(yǔ)句即可。
@Query("select * from cheese where name ==:name ")
fun getCheese(name:String): List<Cheese>?
四、結(jié)語(yǔ)
到這里,Room的基本使用就差不多介紹完成了,那么在實(shí)際開(kāi)發(fā)中,僅僅掌握基本使用,是遠(yuǎn)遠(yuǎn)不夠的,如多表查詢,一對(duì)一、一對(duì)多等關(guān)系的查詢?nèi)绾螌?shí)現(xiàn)?配合Rxjava/LiveData/Paging的使用
請(qǐng)查看 Android Jetpack架構(gòu)組件-Room數(shù)據(jù)庫(kù)查詢藝術(shù)
數(shù)據(jù)庫(kù)的遷移如何實(shí)現(xiàn),,待后續(xù)更新。
本文示例代碼已上傳至Jetpack_Component