Room 數(shù)據(jù)庫框架最全攻略

Room 數(shù)據(jù)庫框架最全攻略

RoomGoogle官方推出的Android Sqlite數(shù)據(jù)庫處理框架儿礼,是子啊Sqlite上提供了一個(gè)抽象層醇份,以便在充分利用 SQLite 的強(qiáng)大功能的同時(shí)空郊,能夠流暢地訪問數(shù)據(jù)庫谷饿。本文的目的旨在對(duì)于Room框架可以快速上手未荒,內(nèi)容分為兩部分,第一部分為數(shù)據(jù)庫的基本操作(增接箫、刪攒读、改、查)辛友,第二部數(shù)據(jù)庫的升級(jí)薄扁,加密

image

基本使用

基本工作的準(zhǔn)備

  • 引入Room依賴 (在Module的build.gradle文件中添加如下依賴)
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'//在此處引入kapt插件
}

...

dependencies {
    ...
    
    def room_version = "2.2.6" // check latest version from docs
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    
    ...
}



以上準(zhǔn)備工作就做的差不多了,接下來開始建立數(shù)據(jù)庫表

  • 建表

    本文已 User 表為例

    @Entity
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        val age: Int = 0废累,
         @PrimaryKey(autoGenerate = true)
        val uid: Int = 0
    )
    

    如上邓梅,一張User表就建好了,如果指定表名九默,默認(rèn)是使用類名作為表明震放,如果要指定表名可以這樣:

    @Entity(tableName = "first_user")
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        var age: Int = 0
      @PrimaryKey(autoGenerate = true)
        val uid: Int = 0
    )
    

    在上面的代碼中,一張first_user的表就建好了驼修。里面有幾個(gè)注解需要說明一些

    • @ColumnInfo(用來指定數(shù)據(jù)庫表中對(duì)應(yīng)的列明殿遂,屬性 name 可以單獨(dú)指定列名衷恭,如果不設(shè)置隙赁,默認(rèn)為變量名作為列名)
    • @PrimaryKey (用來指定主鍵的,該注解修飾的字段屬性必須為 int 榛臼,long等整型耳峦,屬性autoGenerate 可以設(shè)置主鍵自增)

    在實(shí)際使用過中恩静,以上寫法其實(shí)是有問題的,上面代碼指定了 uid做為主鍵蹲坷,并且設(shè)置了自增驶乾,但是該屬性放在構(gòu)造方法的位置,書實(shí)例化User 這個(gè)對(duì)象的時(shí)候循签,這個(gè) uid 的值是必須填寫的级乐。所以要不指定主鍵,讓其自增县匠,該如下實(shí)現(xiàn)

    @Entity(tableName = "first_user")
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        var age: Int = 0
    ) {
        @PrimaryKey(autoGenerate = true)
        var uid: Int = 0
    
    }
    
  • Dao(包含用于訪問數(shù)據(jù)庫的方法)

    @Dao
    interface UserDao {
    }
    

    這個(gè)一個(gè)接口风科,包括了管理數(shù)據(jù)的方法,以 曾刪改查為例

    @Dao
    interface UserDao {
         @Query("SELECT * FROM user")
        fun getUser(): MutableList<User>//查詢所有
        
         @Delete
        fun delete(user: User)//刪除指定的User
        
         @Update(entity = User::class)
        fun updateUser(user: User)//修改指定的User
        
         @Insert
        fun insertUser(vararg users: User)//插入數(shù)據(jù)乞旦,可以是單個(gè)贼穆,也可以是多個(gè)
    }
    
  • AppDatabase Dao管理對(duì)象

        @Database(entities = [User::class],version = 1)
        abstract class AppDatabase : RoomDatabase() {
            abstract fun userDao(): UserDao
            abstract fun carDao():CarDao
            ...
        }
        
    

    在以上代碼中有幾個(gè)注解簡(jiǎn)單說下

    • @Database Marks a class as a RoomDatabase. 標(biāo)記一個(gè)類作為 RoomDatabase,被這個(gè)注解修飾類是個(gè)抽象類兰粉,并且需要繼承 RoomDatabase故痊,上面代碼就是該類的固定寫法
    • entities屬性,該屬性 修飾的是一個(gè)數(shù)組玖姑,該數(shù)組需要加入 被 @Entity 注解修飾的類愕秫,也就是數(shù)據(jù)庫中的表
    • version 屬性浊仆,該屬性制動(dòng)了數(shù)據(jù)庫的版本
  • 創(chuàng)建數(shù)據(jù)庫實(shí)例

    創(chuàng)建上述文件后,使用以下代碼獲取已創(chuàng)建的數(shù)據(jù)庫的實(shí)例

        val db = Room.databaseBuilder(
                    applicationContext,
                    AppDatabase::class.java, "database-name"
                ).build()
        
    

    上述代碼已經(jīng)很清晰的展示了數(shù)據(jù)庫對(duì)象的創(chuàng)建過程豫领,“database-name”,該字符串指定了數(shù)據(jù)庫的名稱舔琅,可以按需求改成自己設(shè)置的名稱

    注意:在單個(gè)進(jìn)程中運(yùn)行等恐,在實(shí)例化 AppDatabase 對(duì)象時(shí)應(yīng)遵循單例設(shè)計(jì)模式。每個(gè)RoomDatabase實(shí)例的成本相當(dāng)高备蚓,如下


object DBHelper {
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).build()
}
  • 測(cè)試

    
    class MainViewModel : ViewModel() {
        fun getUser(): MutableList<User> {
            return DBHelper.db.getUserDao().getUser()
        }
    
        fun insert(vararg users: User) {
            DBHelper.db.getUserDao().insertUser(*users)
        }
    
        fun findByName(firstName: String,lastName:String):User {
            return DBHelper.db.getUserDao().findByName(firstName, lastName)
        }
        fun updateUser(user: User) {
            DBHelper.db.getUserDao().updateUser(user)
        }
    }
    
    ...
    
    //需要說明的是,關(guān)于所有數(shù)據(jù)庫相關(guān)的操作,都需要在子線程中執(zhí)行
    GlobalScope.launch {
                val user = viewModel.getUser()//查詢
                println("第一次取值 = $user")
    
                val insertUser = User("Tom","Json")
                val insertUser1 = User("Tom1","Json1")
                val insertUser2 = User("Tom2","Json2")
                val insertUser3 = User("Tom3","Json3")
                val insertUser4 = User("Tom4","Json4")
    
                                      viewModel.insert(insertUser,insertUser1,insertUser2,insertUser3,insertUser4)//批量插入
                val user1 = viewModel.getUser()//再次查詢
                println(message = "第二次取值 = $user1")
    
                val onlyUser = viewModel.findByName("Tom4", "Json4")//按條件查詢
                onlyUser.lastName = "sunjian"
                viewModel.updateUser(onlyUser)//修改
            }
    
    

    執(zhí)行結(jié)果如下

    2021-03-12 15:16:18.211 30145-30269/com.example.firstapp I/System.out: 第一次取值 = []
    2021-03-12 15:16:18.213 30145-30269/com.example.firstapp I/System.out: 第二次取值 = [User(firstName=Tom, lastName=Json, age=0), User(firstName=Tom1, lastName=Json1, age=0), User(firstName=Tom2, lastName=Json2, age=0), User(firstName=Tom3, lastName=Json3, age=0), User(firstName=Tom4, lastName=Json4, age=0)]
    2021-03-12 15:16:18.215 30145-30269/com.example.firstapp I/System.out: 第三次取值 = [User(firstName=Tom, lastName=Json, age=0), User(firstName=Tom1, lastName=Json1, age=0), User(firstName=Tom2, lastName=Json2, age=0), User(firstName=Tom3, lastName=Json3, age=0), User(firstName=Tom4, lastName=sunjian, age=0)]
    

數(shù)據(jù)庫升級(jí)

在開發(fā)過程中難免會(huì)碰到數(shù)據(jù)庫表結(jié)構(gòu)的改變课蔬,碰到這種情況,我們就需要對(duì)數(shù)據(jù)庫表的結(jié)構(gòu)進(jìn)行升級(jí)郊尝,sqlite支持的數(shù)據(jù)庫表結(jié)構(gòu)的操作說明:

SQLite supports a limited subset of ALTER TABLE. The ALTER TABLE command in SQLite allows the user to rename a table or to add a new column to an existing table. It is not possible to rename a column, remove a column, or add or remove constraints from a table.

大致意思就是說二跋,支持字段的添加和修改,不支持刪除流昏。所以針對(duì)數(shù)據(jù)庫結(jié)構(gòu)的升級(jí)就只是正對(duì)字段的添加和修改

  • 數(shù)據(jù)庫表字段添加

我們正對(duì)之前Entity類進(jìn)行操作扎即,新增加一個(gè)e_mail字段

@Entity()
data class User(
    @ColumnInfo(name = "first_name")
    val firstName: String,
    @ColumnInfo(name = "last_name")
    var lastName: String,
    @ColumnInfo(name = "age")
    var age: Int = 0



) {
    @PrimaryKey(autoGenerate = true)
    var uid: Int = 0

    @ColumnInfo
    var email: String = ""http://新增的數(shù)據(jù)庫字段
}
  • 升級(jí)數(shù)據(jù)庫的版本
@Database(entities = [User::class],version = 2)//版本號(hào)有原來的 1 -> 2
abstract class AppDatabase : RoomDatabase() {

    abstract fun getUserDao(): UserDao

}
  • 數(shù)據(jù)庫對(duì)象中添加遷移配置
 val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2())
        .build()


//此處需要重點(diǎn)說明一下,添加字段之后况凉,需要設(shè)置 NOT NULL屬性谚鄙,而且需要給默認(rèn)值,要不然數(shù)據(jù)庫遷移過程中就會(huì)提示刁绒,創(chuàng)建的數(shù)據(jù)庫信息闷营,和預(yù)期的不一致,從而導(dǎo)致閃退
 class Migration_1_2 : Migration(1,2){
        override fun migrate(database: SupportSQLiteDatabase) {
           database.execSQL("alter table User add column email TEXT NOT NULL DEFAULT ''")
        }
    }

數(shù)據(jù)庫 User表中添加的email字段就成功了知市,接下來說一下傻盟,修改字段,還是以上面的帶嗎為例嫂丙,添加字段之后娘赴,發(fā)現(xiàn) email 這個(gè)字段好像不是很對(duì),要修改成 e_mail奢入,需要修改的內(nèi)容如下:


//1.第一處需要修改的地方  修改字段
@Entity()
data class User(
    @ColumnInfo(name = "first_name")
    val firstName: String,
    @ColumnInfo(name = "last_name")
    var lastName: String,
    @ColumnInfo(name = "age")
    var age: Int = 0



) {
    @PrimaryKey(autoGenerate = true)
    var uid: Int = 0

    @ColumnInfo
    var e_mail: String = ""http://字段重新修改
}

//2.修改版本號(hào)筝闹,記住這個(gè)版本
@Database(entities = [User::class],version = 2)
abstract class AppDatabase : RoomDatabase() {
    abstract fun getUserDao(): UserDao
}
//3.添加遷移配置
object DBHelper {
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2(),Migration_2_3())//注意此處需要傳的參數(shù)是可變參數(shù)腥光,直接添加就行了关顷,不需要把之前的都刪了
        .build()

    class Migration_1_2 : Migration(1,2){
        override fun migrate(database: SupportSQLiteDatabase) {
           database.execSQL("alter table User add column email TEXT NOT NULL DEFAULT ''")
        }

    }
    //修改字段 Migration 這個(gè)類構(gòu)造中傳的字段就是 數(shù)據(jù)庫的升級(jí)前和升級(jí)后的版本號(hào),一定要和 2 中的數(shù)字對(duì)應(yīng)
    class Migration_2_3 : Migration(2,3){
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("alter table User rename column email to e_mail")
        }

    }
}

到此武福,數(shù)據(jù)庫的修改就完成了议双。

數(shù)據(jù)庫加密

在實(shí)際開發(fā)過程過程中,設(shè)計(jì)到安全問題捉片,手機(jī)root過之后平痰,用戶可以隨意拿到數(shù)據(jù)庫文件汞舱,進(jìn)行查看。針對(duì)一些敏感的數(shù)據(jù)庫數(shù)據(jù)宗雇,需要對(duì)其進(jìn)行加密昂芜。本文采用

庫進(jìn)行操作,操作很簡(jiǎn)單赔蒲。

//引入依賴
implementation "net.zetetic:android-database-sqlcipher:4.4.2"
...

//添加配置
 private val factory = SupportFactory("xxxxxx".toByteArray())//此處xxxxxx 用戶根據(jù)自己的情況自己配置
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2(), Migration_2_3())
        .openHelperFactory(factory)//添加factory
        .build()

這樣就可以了,數(shù)據(jù)庫加密就完成了,是不是很簡(jiǎn)單

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泌神,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舞虱,更是在濱河造成了極大的恐慌欢际,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矾兜,死亡現(xiàn)場(chǎng)離奇詭異损趋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)椅寺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門浑槽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人返帕,你說我怎么就攤上這事括荡。” “怎么了溉旋?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵畸冲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我观腊,道長(zhǎng)邑闲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任梧油,我火速辦了婚禮苫耸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘儡陨。我一直安慰自己褪子,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布骗村。 她就那樣靜靜地躺著嫌褪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胚股。 梳的紋絲不亂的頭發(fā)上笼痛,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼缨伊。 笑死摘刑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的刻坊。 我是一名探鬼主播枷恕,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谭胚!你這毒婦竟也來了活尊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤漏益,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后深胳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绰疤,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年舞终,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轻庆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敛劝,死狀恐怖余爆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情夸盟,我是刑警寧澤蛾方,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站上陕,受9級(jí)特大地震影響桩砰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜释簿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一亚隅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庶溶,春花似錦煮纵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至套像,卻和暖如春隘擎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凉夯。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工货葬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留采幌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓震桶,卻偏偏與公主長(zhǎng)得像休傍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹲姐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • AngularJS是什么磨取?AngularJs(后面就簡(jiǎn)稱ng了)是一個(gè)用于設(shè)計(jì)動(dòng)態(tài)web應(yīng)用的結(jié)構(gòu)框架。首先柴墩,它是...
    200813閱讀 1,602評(píng)論 0 3
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,930評(píng)論 2 89
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,184評(píng)論 0 3
  • Room是一個(gè)對(duì)象關(guān)系映射(ORM)庫忙厌。Room抽象了SQLite的使用,可以在充分利用SQLite的同時(shí)訪...
    tuacy閱讀 54,009評(píng)論 16 109
  • ### 什么是Vue.js + Vue.js 是目前最火的一個(gè)前端框架江咳,React是最流行的一個(gè)前端框架(Reac...
    JLong閱讀 1,062評(píng)論 0 0