Jetpack組件之Room使用

Room 是Jetpack中的ORM組件并蝗,Room 可以簡(jiǎn)化SQLite數(shù)據(jù)庫(kù)操作诱桂。
Room 在 SQLite 上提供了一個(gè)抽象層,以便在充分利用 SQLite 的強(qiáng)大功能的同時(shí)豪诲,能夠流暢地訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。

  • 添加依賴(lài)
// 使用kotlin開(kāi)發(fā)必須使用插件kapt添加room-compiler
plugins {
    id 'kotlin-kapt'
}
dependencies {
    val roomVersion = "2.4.0"

    // Java開(kāi)發(fā)
    // 基礎(chǔ)功能
    implementation("androidx.room:room-runtime:$roomVersion")
    annotationProcessor("androidx.room:room-compiler:$roomVersion")
    
    // Kotlin開(kāi)發(fā)(添加上邊的kotlin-kapt插件)
    // 支持協(xié)程(包含基礎(chǔ)功能)
    implementation("androidx.room:room-ktx:$roomVersion")
    // 必須引用
    kapt("androidx.room:room-compiler:$roomVersion")
}

Room包含三個(gè)組件挂绰,Entity屎篱、DaoDatabase

    // 該注解代表數(shù)據(jù)庫(kù)一張表葵蒂,tableName為該表名字交播,不設(shè)置則默認(rèn)類(lèi)名
    // 注解必須有!践付!tableName可以不設(shè)置
    @Entity(tableName = "User")
    data class User(
        // 該標(biāo)簽指定該字段作為表的主鍵, 自增長(zhǎng)秦士。注解必須有!永高!
        @PrimaryKey val id: Int? = null,
        
        // 該注解設(shè)置當(dāng)前屬性在數(shù)據(jù)庫(kù)表中的列名和類(lèi)型隧土,注解可以不設(shè)置,不設(shè)置默認(rèn)列名和屬性名相同
        @ColumnInfo(name = "content", typeAffinity = ColumnInfo.TEXT) 
        val content: String?

        // 該標(biāo)簽用來(lái)告訴系統(tǒng)忽略該字段或者方法乏梁,顧名思義:不生成列
        @Ignore
    )
  • 創(chuàng)建Dao次洼,Dao一定是個(gè)接口或抽象類(lèi)。一個(gè)Entity代表著一張表遇骑,而每張表都需要一個(gè)Dao對(duì)象卖毁,方便對(duì)這張表進(jìn)行增刪改查
    @Dao
    interface UserDao {
        @Query("SELECT * FROM User")
        fun getAll(): List<User>

        @Query("SELECT * FROM User WHERE content LIKE :content LIMIT 1")
        fun findByContent(content: String): User

        @Insert
        fun insertAll(vararg users: User)

        @Delete
        fun delete(user: User)
    }
  • 創(chuàng)建Database,抽象類(lèi)落萎,繼承RoomDatabase
@Database(
    // 指定該數(shù)據(jù)庫(kù)有哪些表亥啦,若需建立多張表练链,以逗號(hào)相隔開(kāi)
    entities = [User::class],
    // 指定數(shù)據(jù)庫(kù)版本號(hào),后續(xù)數(shù)據(jù)庫(kù)的升級(jí)正是依據(jù)版本號(hào)來(lái)判斷的
    version = 1
)
abstract class AppDatabase : RoomDatabase() {

    // 提供所有Dao媒鼓,對(duì)一一對(duì)應(yīng)的數(shù)據(jù)庫(kù)表進(jìn)行操作
    abstract fun getUserDao(): UserDao

    companion object {
    
        private const val DB_NAME = "app_name.db"

        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this){
                val instance = Room.databaseBuilder(
                        context.applicationContext, AppDatabase::class.java,
                        DB_NAME
                    ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
  • 代碼中調(diào)用
class DataRepository{

    companion object {

        @Volatile
        private var instance: DataRepository? = null

        @Synchronized
        fun getInstance(): DataRepository {
            if (instance == null) {
                instance = DataRepository()
            }
            return instance!!
        }
    }

    /**
     * 查詢(xún)用戶(hù)信息
     */
    fun getUser(context: Context): User? {
        val userList = AppDatabase.getDatabase(context).getUserDao().getAll()
        if (userList.isNullOrEmpty()) {
            return null
        }

        val content = userList[0].content

        return Gson().fromJson(content, UserInfoBean::class.java)
    }

    /**
     * 儲(chǔ)存用戶(hù)信息
     *
     * @param content 用戶(hù)信息Bean Json
     */
    fun saveUser(context: Context, content: String?) {
        val dao = AppDatabase.getDatabase(context).getUserDao()

        val queryInfo = dao.findFirst()

        if (queryInfo == null) {
            val user = User(content = content)
            dao.insert(user)
        } else {
            queryInfo.content = content
            dao.update(user)
        }
    }
}
  • 使用注意點(diǎn)
1. cannot find implementation for com.aheading.request.database.AppDatabase. AppDatabase_Impl does not exist

譯:無(wú)法找到com.aheading.request.database.AppDatabase的實(shí)現(xiàn)错妖。AppDatabase_Impl不存在。即數(shù)據(jù)庫(kù)創(chuàng)建失敗

解決方案:

1.1 檢查所有注解是否添加

@Entity 
@PrimaryKey
@Dao
@Database(
    entities = [User::class],
    version = 1
)

1.2 檢查頂部依賴(lài)是否配置正確暂氯。若多模塊開(kāi)發(fā),Base模塊中已配置相關(guān)依賴(lài)痴施,其他模塊只要用到了Database,也需要在build.gradle中添加如下依賴(lài)包究流,不需要功能依賴(lài):

plugins {
    id 'kotlin-kapt'
}

dependencies {
    kapt("androidx.room:room-compiler:$roomVersion")
}
2. Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

譯:無(wú)法在主線(xiàn)程上訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)辣吃,因?yàn)樗赡軙?huì)鎖定UI很長(zhǎng)一段時(shí)間。

解決方案:

方案一:創(chuàng)建數(shù)據(jù)庫(kù)時(shí)設(shè)置允許主線(xiàn)程訪(fǎng)問(wèn) allowMainThreadQueries()神得,不推薦灯节!

Room.databaseBuilder(
    AppHelper.mContext, AppDatabase::class.java,
    DB_NAME
)
    // 禁用Room的主線(xiàn)程查詢(xún)檢查(慎用C喙馈!9选)
    .allowMainThreadQueries()
    .build()

方案二:子線(xiàn)程中調(diào)用,我是使用了協(xié)程進(jìn)行操作缝左。

viewModelScope.launch(Dispatchers.IO) {
    val localUser = DataRepository.getInstance().getUser(context)
    
    user.postValue(localUser)

    withContext(Dispatchers.Main) {
        // 切換到主線(xiàn)程執(zhí)行UI相關(guān)操作
        ...
    }
}
3. Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

譯:Room無(wú)法驗(yàn)證數(shù)據(jù)完整性渺杉。看起來(lái)您已經(jīng)更改了架構(gòu)是越,但忘記更新版本號(hào)。你可以通過(guò)增加版本號(hào)來(lái)解決這個(gè)問(wèn)題浦徊。就是你修改了數(shù)據(jù)庫(kù)天梧,但是沒(méi)有升級(jí)數(shù)據(jù)庫(kù)的版本。

解決方案:

第一步:更新數(shù)據(jù)庫(kù)的注解配置(entitys和版本號(hào))冕香,我這里新增表 PersonTable

@Database(
    entities = [User::class,PersonTable::class],
    version = 2
)

第二步:添加Migration
數(shù)據(jù)庫(kù)升級(jí)用到的sql語(yǔ)句悉尾,不用自己寫(xiě),去自動(dòng)生成的類(lèi)中copy陨收。否則鸵赖,自己寫(xiě)和自動(dòng)生成的語(yǔ)句不一致的話(huà),會(huì)報(bào)錯(cuò)它褪。默認(rèn)生成的語(yǔ)句在你的 XxxDatabase_Impl 這個(gè)類(lèi)中茫打,例:AppDatabase_Impl

// AppDatabase_Impl中代碼
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `student` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `age` TEXT NOT NULL, `roomId` INTEGER NOT NULL)");
  _db.execSQL("CREATE TABLE IF NOT EXISTS `class_room` (`class_id` INTEGER NOT NULL, `class_name` TEXT NOT NULL, PRIMARY KEY(`class_id`))");
  _db.execSQL("CREATE TABLE IF NOT EXISTS `address` (`addressId` INTEGER NOT NULL, `addressName` TEXT NOT NULL, PRIMARY KEY(`addressId`))");
  _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
  _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7cbdd6263025181ec070edd36e1118eb')");
}

// 1. 新增數(shù)據(jù)庫(kù)版本升級(jí)Migration
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(
            // 添加IF NOT EXISTS和IF EXISTS沒(méi)壞處
            "CREATE TABLE IF NOT EXISTS 'PersonTable'('id' INTEGER, 'content' TEXT, PRIMARY KEY('id'))"
        )
    }
}

// 2. 數(shù)據(jù)庫(kù)新建處addMigrations
Room.databaseBuilder(
    AppHelper.mContext, AppDatabase::class.java,
    DB_NAME
)
    .addMigrations(MIGRATION_1_2)
    .build()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末老赤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抬旺,更是在濱河造成了極大的恐慌,老刑警劉巖汉柒,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件责鳍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡正塌,警方通過(guò)查閱死者的電腦和手機(jī)啃洋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)问裕,“玉大人孵坚,你說(shuō)我怎么就攤上這事窥淆∥¤荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵词裤,是天一觀的道長(zhǎng)鳖宾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)渔肩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任周偎,我火速辦了婚禮撑帖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袍嬉。我一直安慰自己灶平,他們只是感情好箍土,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瞒爬,像睡著了一般沟堡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上航罗,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天粥血,我揣著相機(jī)與錄音酿箭,去河邊找鬼趾娃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抬闷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讥耗,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼疹启,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挣磨?” 一聲冷哼從身側(cè)響起荤懂,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晤锥,沒(méi)想到半個(gè)月后廊宪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壕翩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年傅寡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荐操。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡托启,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驾中,到底是詐尸還是另有隱情模聋,我是刑警寧澤唠亚,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站灶搜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏前酿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一罢维、第九天 我趴在偏房一處隱蔽的房頂上張望丙挽。 院中可真熱鬧肺孵,春花似錦颜阐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)紫新。三九已至萨赁,卻和暖如春弊琴,著一層夾襖步出監(jiān)牢的瞬間杖爽,已是汗流浹背紫皇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工聪铺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留化焕,地道東北人铃剔。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凤类,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佃延,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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