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屎篱、Dao、Database
-
新建Entity
// 該注解代表數(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()