Room-資料持久化儲存(入門)

孟老闆發表於2021-06-28

@


# 前言

官方簡介:

Room 永續性庫在 SQLite 的基礎上提供了一個抽象層,讓使用者能夠在充分利用 SQLite 的強大功能的同時,獲享更強健的資料庫訪問機制。

引入庫:

implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
//kotlin 擴充套件庫
implementation "androidx.room:room-ktx:2.3.0"

配置 build:

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments += mapOf(
                    "room.schemaLocation" to "$projectDir/schemas",
                    "room.incremental" to "true",
                    "room.expandProjection" to "true"
                )
            }
        }
    }
}

提示:以下是本篇文章正文內容,下面案例可供參考

一、簡單使用

萬事開頭難, 好記性不如爛筆頭. 看十遍不如敲一遍.
所以 我們們先把程式碼敲起來.

1.Entity

@Entity
class RoomTwoEntity {
    @PrimaryKey
    var id: String = ""
    @ColumnInfo
    var nickname: String? = null
    @ColumnInfo
    var sex: Int = 0

    @Ignore
    var like = false
}
  • @Entity: 表示資料庫中的表
  • @ColumnInfo: 表示table中的欄位
  • @Ignore: 表示忽略該欄位, (不對映到表中)

細節後面再講

2.Dao

@Dao
interface RoomTwoDao {
    @Query("select * from RoomTwoEntity")
    fun get(): MutableList<RoomTwoEntity>

    @Insert(onConflict = OnConflictStrategy.REPLACE)    //當衝突時: ABORT,取消;  REPLACE,替換;  IGNORE,忽略;
    fun add(entity: RoomTwoEntity)

    @Insert(onConflict = OnConflictStrategy.REPLACE)    //當衝突時: ABORT,取消;  REPLACE,替換;  IGNORE,忽略;
    fun addList(list: MutableList<RoomTwoEntity>)

    @Delete
    fun delete(entity: RoomTwoEntity)

    @Update
    fun update(entity: RoomTwoEntity)

    @Query("delete  from RoomTwoEntity where id = :id ")    //刪除也可以用 query
    fun deleteById(id: String)
}

這程式碼是不是很好閱讀!
普通增刪改只需要一個註解. 查詢需要寫 sql


3.DataBase

@Database(entities = [RoomTwoEntity::class], version = 1)
abstract class RoomTestDatabase : RoomDatabase() {
    abstract fun roomTwoDao(): RoomTwoDao

    companion object {
        private var instance: RoomTestDatabase? = null
        fun getInstance(context: Context): RoomTestDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context.applicationContext,
                    RoomTestDatabase::class.java,
                    "Test.db" //資料庫名稱
                )
//                    .allowMainThreadQueries()   //主執行緒中執行
                    .fallbackToDestructiveMigration() //資料穩定前, 重建.
//                    .addMigrations(MIGRATION_1_2) //版本升級
                    .build()
            }
            return instance!!
        }
    }
}

4.使用

注意: 程式碼要在子執行緒中執行. 或者方便測試時開啟 .allowMainThreadQueries()

//建立 10 條資料
private fun createTen(){
    val list = mutableListOf<RoomTwoEntity>()
    repeat(10){
        list.add(RoomTwoEntity().apply {
            id = "room$it"
            nickname = "名字$it"
        })
    }
    RoomTestDatabase.getInstance(mActivity).roomTwoDao().addList(list)
}

//查詢資料
private fun query(): MutableList<RoomTwoEntity>{
    return RoomTestDatabase.getInstance(mActivity).roomTwoDao().get()
}

//例如
GlobalScope.launch(Dispatchers.IO) {
    createTen()
}

二、引數解析

1.Entity

1.1 @Entity

欄位名 意義用法
tableName (String) table名, 預設Entity類名.
indices (Index[]) 索引, 搞過資料庫的都懂吧.
單列:indices = arrayOf(Index(value = ["last_name"]))
多列:indices = arrayOf(Index(value = ["last_name", "address"]))
多個索引:indices = arrayOf(Index(value = ["last_name"]),Index(value = ["address"]))
Index.unique = true 索引項唯一
inheritSuperIndices (boolean) 是否繼承父類的索引
primaryKeys (String[]) 主鍵. 單主鍵的時候可以定義在欄位上. 複合主鍵的時候定義在類上.
它應該也能定義父類的欄位.
示例: primaryKeys = arrayOf("firstName", "lastName")
foreignKeys 外來鍵
ignoredColumns (String[]) 忽略欄位, 更容易的忽略父類欄位

1.2 @ColumnInfo
這個註解預設可以省略

欄位名 意義用法
name 列名, 預設就是屬性名
typeAffinity 欄位型別, 預設按屬性型別自動生成. 還有 TEXT,INTEGER,REAL,BLOB
index (boolean) 是否生成單列索引, 預設false
collate 建表時 列的排序
defaultValue 列的預設值

1.3 @PrimaryKey

主鍵必須有, 且必須註解標出
每個實體必須將至少 1 個欄位定義為主鍵。即使只有 1 個欄位,您仍然需要為該欄位新增 @PrimaryKey 註釋。此外,如果您想讓 Room 為實體分配自動 ID,則可以設定 @PrimaryKey 的 autoGenerate 屬性。如果實體具有複合主鍵,您可以使用 @Entity 註釋的 primaryKeys 屬性.

2.Dao

2.1 onConflict

@Insert @Update 時:
新增或修改時, 假設與原有行資料衝突(例如主鍵衝突). 時的處理方式

用法
OnConflictStrategy.REPLACE 覆蓋,替換 原有資料
OnConflictStrategy.ABORT (預設模式) 嚴格模式, 會讓事務失敗並回滾
OnConflictStrategy.IGNORE 非嚴格模式, 會忽略衝突行, 然後繼續執行其他操作

2.2 還可以這樣用:

@Dao
interface MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)	// java: (User... users)

    @Insert
    fun insertBothUsers(user1: User, user2: User) //多實體

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)

    @Update
    //可以讓它返回一個 Int, 代表資料庫中更新的行數
    fun updateUsers(vararg users: User): Int

    @Delete
    fun deleteUsers(vararg users: User): Int	//返回行數
}

2.3 @Query

以下搬運自官方文章:
@Query 是 DAO 類中使用的主要註釋。它允許您對資料庫執行讀/寫操作。每個 @Query 方法都會在編譯時進行驗證,因此如果查詢出現問題,則會發生編譯錯誤,而不是執行時失敗。

Room 還會驗證查詢的返回值,以確保當返回的物件中的欄位名稱與查詢響應中的對應列名稱不匹配時,Room 可以通過以下兩種方式之一提醒您:

  • 如果只有部分欄位名稱匹配,則會發出警告。
  • 如果沒有任何欄位名稱匹配,則會發出錯誤。

3.查詢方式

它可以這樣查: 冒號後寫引數名.

@Query("SELECT * FROM user WHERE age > :minAge")
fun loadAllUsersOlderThan(minAge: Int): Array<User>

也可以這樣查: 只查部分欄位

@Query("SELECT first_name, last_name FROM user")
fun loadFullName(): List<NameTuple>

還可以這樣查: 傳入引數集合

@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
fun loadUsersFromRegions(regions: List<String>): List<NameTuple>

更可以這樣查: 連表查詢,複雜查詢等

@Query(
    "SELECT user.name AS userName, pet.name AS petName " +
    "FROM user, pet " +
    "WHERE user.id = pet.user_id"
)
fun loadUserAndPetNames(): LiveData<List<UserPet>>

// You can also define this class in a separate file.
data class UserPet(val userName: String?, val petName: String?)

雖然博主沒測, 但這裡 UserPet 估計是不需要 @Entity 的;

開啟事務
@Transaction

@Dao
abstract class UsersDao {
	@Transaction
	open suspend fun setLoggedInUser(loggedInUser: User) {
	    deleteUser(loggedInUser)
	    insertUser(loggedInUser)
	}
	
	@Query("DELETE FROM users")
	abstract fun deleteUser(user: User)
	
	@Insert
	abstract suspend fun insertUser(user: User)
}

因為DB操作必須要在子執行緒:
可以將 suspend Kotlin 關鍵字新增到 DAO 方法中,以使用 Kotlin 協程功能使這些方法成為非同步方法。這樣可確保不會在主執行緒上執行這些方法。

就寫到這把, 篇幅有點長了, 剩下的下一篇再講.

總結

room的使用可比直接用SQLite簡單多了. 而且方便升級. 還能夠配合Paging, 配合LiveData, FLow等使用.

上一篇: Paging3-分頁資料載入庫(結合room)
下一篇: Room-資料持久化儲存(進階)

相關文章