Room數(shù)據(jù)庫(kù)原理及使用

一、Room數(shù)據(jù)庫(kù)的大三組件

Database:即數(shù)據(jù)庫(kù)芳杏。擴(kuò)展了RoomDatabase的抽象類(lèi)矩屁。可以通過(guò)Room獲得它的一個(gè)實(shí)例爵赵。databaseBuilder或Room.inMemoryDatabaseBuilder吝秕。
Dao:數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象,是Room的主要組件空幻,負(fù)責(zé)定義訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的方法烁峭。
Entity:實(shí)體類(lèi),代表一個(gè)表結(jié)構(gòu)秕铛。

二约郁、實(shí)現(xiàn)原理

Room數(shù)據(jù)庫(kù)實(shí)現(xiàn)原理

在數(shù)據(jù)庫(kù)DataBase中想要訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的操作,需要獲取操作Dao的對(duì)象但两,而Dao會(huì)給DataBase返回一個(gè)entity的對(duì)象鬓梅。當(dāng)我們用Room數(shù)據(jù)庫(kù)進(jìn)行存儲(chǔ)時(shí),其實(shí)存儲(chǔ)的并非要存儲(chǔ)的具體對(duì)象镜遣,而是對(duì)象對(duì)應(yīng)的一些信息己肮。因此士袄,我們引入Repository倉(cāng)庫(kù)類(lèi)用于操作Room,而我們給外部暴露的是viewModel類(lèi)谎僻,因此娄柳,viewModel需要持有Repository對(duì)象。此外,Repository與viewModel之間也需要context傳遞上下文信息艘绍。

三赤拒、優(yōu)點(diǎn)

  • 針對(duì) SQL 查詢(xún)的編譯時(shí)驗(yàn)證
  • 可最大限度減少重復(fù)和容易出錯(cuò)的樣板代碼的方便注解
  • 簡(jiǎn)化了數(shù)據(jù)庫(kù)遷移路徑

四、使用方式(以創(chuàng)建一個(gè)相冊(cè)數(shù)據(jù)庫(kù)為例)

  1. 聲明依賴(lài)項(xiàng)
     def room_version = "2.3.0"
     implementation "androidx.room:room-runtime:$room_version"
     annotationProcessor "androidx.room:room-compiler:$room_version"
  1. 創(chuàng)建相冊(cè)表實(shí)體類(lèi)Entity
import androidx.room.Entity
import androidx.room.PrimaryKey

const val ALBUM_TYPE_IMAGE = 0
const val ALBUM_TYPE_VIDEO = 1
//默認(rèn)表名就是類(lèi)名
//@Entity(tableName = "album_table")
@Entity
data class Album (

    @PrimaryKey(autoGenerate = true)//主鍵自增長(zhǎng)取消诱鞠,因?yàn)槭莝tring類(lèi)型
    val id:Int,
    var albumName:String,
    //@ColumnInfo(name ="cover_url") 表名和屬性名的轉(zhuǎn)換
    var coverUrl:String,
    var number:Int,
    val type:Int = ALBUM_TYPE_IMAGE
)

//縮略圖表
@Entity
data class ThumbImage(
    @PrimaryKey(autoGenerate = false)
    val imageName:String,
    //外鍵
    val albumId: Int
)
  1. 創(chuàng)建Dao
import androidx.room.*
import java.util.concurrent.Flow

@Dao
interface AlbumDao {
    //創(chuàng)建相冊(cè) 數(shù)據(jù)庫(kù)操作都耗時(shí)suspend
    //@Insert(onConflict = OnConflictStrategy.)解決沖突
    @Insert
    fun insertAlbum(album: Album)
    //刪除一個(gè)相冊(cè)
    @Delete
    fun deleteAlbum(album: Album)
    //刪除多個(gè)相冊(cè) 可變數(shù)組
    @Delete
    fun deleteAlbums(vararg album: Album)
    //更新相冊(cè)
    @Update
    fun updateAlbum(album: Album)
    //查詢(xún)所有相冊(cè)信息
    @Query("select * from Album where type = :type")
    fun getAllAlbumsWithType(type:Int):kotlinx.coroutines.flow.Flow<List<Album>>

    /**------------------------相片--------------------------**/
    //插入一張圖
    @Insert
    fun insertImage(thumbImage: ThumbImage)
    //插入多張圖
    @Insert
    fun insertImages(vararg thumbImage: ThumbImage)
    //刪除一張圖
    @Delete
    fun deleteImage(thumbImage: ThumbImage)
    //刪除多張圖
    @Delete
    fun deleteImages(vararg thumbImage: ThumbImage)


}
  1. 創(chuàng)建數(shù)據(jù)庫(kù)DataBase
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(
    entities = [Album::class,ThumbImage::class],
    version = 1,
    exportSchema = false //是否導(dǎo)出
    )
abstract class AlbumDatabase:RoomDatabase() {
    //獲取一系列數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)的接口實(shí)現(xiàn)類(lèi)
    abstract fun albumDao():AlbumDao

    //訪(fǎng)問(wèn)靜態(tài)屬性挎挖,提供單例對(duì)象
    //有就返回,沒(méi)有就加鎖再返回
    companion object{
        private var INSTANCE:AlbumDatabase ? = null
        fun getInstance(context: Context):AlbumDatabase{
            if (INSTANCE != null){
                return INSTANCE!!
            }
            synchronized(this){
                if (INSTANCE == null){
                    INSTANCE = Room.databaseBuilder(
                        context,
                        AlbumDatabase::class.java,
                        "album_db"
                    ).build()
                }
                return INSTANCE!!
            }
        }
    }
}

5.創(chuàng)建Repository給viewModel提供訪(fǎng)問(wèn)方法

import android.content.Context
import kotlinx.coroutines.flow.Flow

class Repository(context: Context) {
    private var albumDao:AlbumDao

    init {
        albumDao = AlbumDatabase.getInstance(context).albumDao()
    }

    //加載相冊(cè)
    suspend fun loadAlbumWithType(type:Int): Flow<List<Album>> {
        return albumDao.getAllAlbumsWithType(type)
    }

    //插入相冊(cè)
    suspend fun addAlbum(album: Album){
        albumDao.insertAlbum(album)
    }
}
  1. viewModel持有Repository航夺,給外部提供操作方法
package com.example.privatealbum.db

import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.privatealbum.DEFAULT_COVER_URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

//與viewModel的區(qū)別是強(qiáng)制傳一個(gè)參數(shù),擁有application即擁有context
class SharedViewModel(application: Application)
    :AndroidViewModel(application){
    //保存所有相冊(cè)信息
    var imageAlbumList = MutableLiveData<List<Album>>(emptyList())
    var videoAlbumList = MutableLiveData<List<Album>>(emptyList())

    //倉(cāng)庫(kù)對(duì)象
    val repository = Repository(application.applicationContext)
    //保存當(dāng)前添加相冊(cè)的類(lèi)型
    var type = ALBUM_TYPE_IMAGE
    //相冊(cè)刪除按鈕默認(rèn)不顯示 有內(nèi)容顯示
    var shouldShowDeleteInAlbum = MutableLiveData(false)
    private var  deleteAlbumList = arrayListOf<Album>()

    //添加需要?jiǎng)h除的相冊(cè)
    fun addAlbumToDeleteList(album: Album){
        deleteAlbumList.add(album)
        shouldShowDeleteInAlbum.postValue(true)
    }
    fun deleteAlbumFromDeleteList(album: Album){
        deleteAlbumList.remove(album)
        shouldShowDeleteInAlbum.postValue(deleteAlbumList.size > 0)
    }

    //獲取相冊(cè)
    fun loadAlbumsWithType(albumType:Int){
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.loadAlbumWithType(albumType)
            result.collectLatest {
                if (albumType == ALBUM_TYPE_IMAGE){
                    imageAlbumList.postValue(it)
                }else{
                    videoAlbumList.postValue(it)
                }
            }

        }
    }
    //插入相冊(cè)
    fun addAlbum(name:String,type: Int){
        viewModelScope.launch(Dispatchers.IO) {
            val album = Album(
                0,
                name,
                getApplication<Application>().DEFAULT_COVER_URL,
                0,
                type = type
            )
            repository.addAlbum(album)
        }
    }
}

五蕉朵、注意事項(xiàng)

  • Room 在創(chuàng)建數(shù)據(jù)庫(kù)時(shí),不需要手動(dòng)編寫(xiě) SQL 語(yǔ)句創(chuàng)建表阳掐,它會(huì)根據(jù)關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)創(chuàng)建對(duì)應(yīng)的表始衅。在定義數(shù)據(jù)庫(kù)時(shí),通過(guò) @Database 注解的 entities 參數(shù)指定數(shù)據(jù)庫(kù)中關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)(即所包含的數(shù)據(jù)表)缭保。
  • 數(shù)據(jù)庫(kù)關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)必須是 @Entity 注解標(biāo)注的汛闸;
  • 數(shù)據(jù)庫(kù)新增數(shù)據(jù)表時(shí),需要新建數(shù)據(jù)實(shí)體類(lèi)艺骂,并將實(shí)體類(lèi)添加到數(shù)據(jù)庫(kù)定義的關(guān)聯(lián)實(shí)體類(lèi)列表中诸老;
  • 數(shù)據(jù)庫(kù)只在首次創(chuàng)建數(shù)據(jù)庫(kù)時(shí),根據(jù)定義的關(guān)聯(lián)數(shù)據(jù)庫(kù)實(shí)體類(lèi)創(chuàng)建表钳恕,無(wú)需編寫(xiě) SQL 語(yǔ)句别伏;
  • 應(yīng)用在新版本添加表(或更改表結(jié)構(gòu)),需要進(jìn)行數(shù)據(jù)庫(kù)遷移(也就是常說(shuō)的數(shù)據(jù)庫(kù)升級(jí))忧额,否則數(shù)據(jù)庫(kù)不會(huì)創(chuàng)建新表(或更改表結(jié)構(gòu))畸肆。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宙址,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌调卑,老刑警劉巖抡砂,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異恬涧,居然都是意外死亡注益,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)溯捆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丑搔,“玉大人厦瓢,你說(shuō)我怎么就攤上這事∑≡拢” “怎么了煮仇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)谎仲。 經(jīng)常有香客問(wèn)我浙垫,道長(zhǎng),這世上最難降的妖魔是什么郑诺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任夹姥,我火速辦了婚禮,結(jié)果婚禮上辙诞,老公的妹妹穿的比我還像新娘辙售。我一直安慰自己,他們只是感情好飞涂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布旦部。 她就那樣靜靜地躺著,像睡著了一般封拧。 火紅的嫁衣襯著肌膚如雪志鹃。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天泽西,我揣著相機(jī)與錄音曹铃,去河邊找鬼。 笑死捧杉,一個(gè)胖子當(dāng)著我的面吹牛陕见,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播味抖,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼孟辑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了夺姑?” 一聲冷哼從身側(cè)響起蛹锰,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熔脂,沒(méi)想到半個(gè)月后佩研,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霞揉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年旬薯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片适秩。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绊序,死狀恐怖硕舆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骤公,我是刑警寧澤抚官,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站淋样,受9級(jí)特大地震影響耗式,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜趁猴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一刊咳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧儡司,春花似錦娱挨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碉碉,卻和暖如春柴钻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垢粮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工贴届, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜡吧。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓毫蚓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親昔善。 傳聞我的和親對(duì)象是個(gè)殘疾皇子元潘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容