Room数据库原理及使用

一、Room数据库的大三组件

Database:即数据库。扩展了RoomDatabase的抽象类。可以通过Room获得它的一个实例。databaseBuilder或Room.inMemoryDatabaseBuilder。
Dao:数据访问对象,是Room的主要组件,负责定义访问数据库的方法。
Entity:实体类,代表一个表结构。

二、实现原理

Room数据库实现原理

在数据库DataBase中想要访问数据库的操作,需要获取操作Dao的对象,而Dao会给DataBase返回一个entity的对象。当我们用Room数据库进行存储时,其实存储的并非要存储的具体对象,而是对象对应的一些信息。因此,我们引入Repository仓库类用于操作Room,而我们给外部暴露的是viewModel类,因此,viewModel需要持有Repository对象。此外,Repository与viewModel之间也需要context传递上下文信息。

三、优点

  • 针对 SQL 查询的编译时验证
  • 可最大限度减少重复和容易出错的样板代码的方便注解
  • 简化了数据库迁移路径

四、使用方式(以创建一个相册数据库为例)

  1. 声明依赖项
     def room_version = "2.3.0"
     implementation "androidx.room:room-runtime:$room_version"
     annotationProcessor "androidx.room:room-compiler:$room_version"
  1. 创建相册表实体类Entity
import androidx.room.Entity
import androidx.room.PrimaryKey

const val ALBUM_TYPE_IMAGE = 0
const val ALBUM_TYPE_VIDEO = 1
//默认表名就是类名
//@Entity(tableName = "album_table")
@Entity
data class Album (

    @PrimaryKey(autoGenerate = true)//主键自增长取消,因为是string类型
    val id:Int,
    var albumName:String,
    //@ColumnInfo(name ="cover_url") 表名和属性名的转换
    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. 创建Dao
import androidx.room.*
import java.util.concurrent.Flow

@Dao
interface AlbumDao {
    //创建相册 数据库操作都耗时suspend
    //@Insert(onConflict = OnConflictStrategy.)解决冲突
    @Insert
    fun insertAlbum(album: Album)
    //删除一个相册
    @Delete
    fun deleteAlbum(album: Album)
    //删除多个相册 可变数组
    @Delete
    fun deleteAlbums(vararg album: Album)
    //更新相册
    @Update
    fun updateAlbum(album: Album)
    //查询所有相册信息
    @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. 创建数据库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 //是否导出
    )
abstract class AlbumDatabase:RoomDatabase() {
    //获取一系列数据库访问的接口实现类
    abstract fun albumDao():AlbumDao

    //访问静态属性,提供单例对象
    //有就返回,没有就加锁再返回
    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.创建Repository给viewModel提供访问方法

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

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

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

    //加载相册
    suspend fun loadAlbumWithType(type:Int): Flow<List<Album>> {
        return albumDao.getAllAlbumsWithType(type)
    }

    //插入相册
    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的区别是强制传一个参数,拥有application即拥有context
class SharedViewModel(application: Application)
    :AndroidViewModel(application){
    //保存所有相册信息
    var imageAlbumList = MutableLiveData<List<Album>>(emptyList())
    var videoAlbumList = MutableLiveData<List<Album>>(emptyList())

    //仓库对象
    val repository = Repository(application.applicationContext)
    //保存当前添加相册的类型
    var type = ALBUM_TYPE_IMAGE
    //相册删除按钮默认不显示 有内容显示
    var shouldShowDeleteInAlbum = MutableLiveData(false)
    private var  deleteAlbumList = arrayListOf<Album>()

    //添加需要删除的相册
    fun addAlbumToDeleteList(album: Album){
        deleteAlbumList.add(album)
        shouldShowDeleteInAlbum.postValue(true)
    }
    fun deleteAlbumFromDeleteList(album: Album){
        deleteAlbumList.remove(album)
        shouldShowDeleteInAlbum.postValue(deleteAlbumList.size > 0)
    }

    //获取相册
    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)
                }
            }

        }
    }
    //插入相册
    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)
        }
    }
}

五、注意事项

  • Room 在创建数据库时,不需要手动编写 SQL 语句创建表,它会根据关联的数据实体类创建对应的表。在定义数据库时,通过 @Database 注解的 entities 参数指定数据库中关联的数据实体类(即所包含的数据表)。
  • 数据库关联的数据实体类必须是 @Entity 注解标注的;
  • 数据库新增数据表时,需要新建数据实体类,并将实体类添加到数据库定义的关联实体类列表中;
  • 数据库只在首次创建数据库时,根据定义的关联数据库实体类创建表,无需编写 SQL 语句;
  • 应用在新版本添加表(或更改表结构),需要进行数据库迁移(也就是常说的数据库升级),否则数据库不会创建新表(或更改表结构)。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容