Android Room使用

簡介

Room庫在SQLite上提供了一個抽象層,允許在充分利用SQLite的功能的同時進行更健壯的數據庫訪問。可以在運行的app設備上創建一個應用數據緩存,這個緩存作爲app的唯一數據來源,不管用戶是否有連接網絡,都允許用戶在app中查看相關信息。
Room有三個主要的組成部分:Database,Entity,DAO。

  • Database:數據庫容器,並作爲到應用程序的持久關係數據的底層連接的主要訪問點。

    使用@Database註釋的類應該滿足以下條件:

    • 擴展RoomDatabase的抽象類
    • 在註釋中包含與數據庫關聯的實體列表
    • 包含一個具有0個參數的抽象方法,並返回用@Dao註釋的類

在運行時,您可以通過調用Room.databaseBuilder()或Room.inMemoryDatabaseBuilder()來獲取數據庫實例。

  • Entity:表示數據庫中的表。
  • DAO:包含用於訪問數據庫的方法。

圖片
Room不同組成的相關關係圖

引用

在project的build.gradle添加google的maven倉庫

allprojects {
    repositories {
        google()
        jcenter()
    }
}

在module的build.gradle添加Room的引用,Room還提供了Rxjava2和Guava的數據處理

dependencies {
    def room_version = "2.1.0-alpha07"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"

    // optional - RxJava support for Room
    implementation "androidx.room:room-rxjava2:$room_version"

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // Test helpers
    testImplementation "androidx.room:room-testing:$room_version"
}

Enity

下面定義一個User的實體

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "second_name") val secondName: String?,
    @Ignore val desc: String?
)
  • **表名:**Room使用實體類的名字作爲數據庫表的名字,如果想更改表名,可以使用@Entity(tableName = “users”)更改。
  • **主鍵:**每個實體都需要定義最少一個主鍵,可以直接在字段使用@PrimaryKey標註主鍵,想Room分配自動ID可使用autoGenerate屬性,也可以使用複合主鍵@Entity(primaryKeys = [“second_name”,“first_name” ])
  • **字段名:**字段的名字默認的也是類中屬性的名字如果想設置其他名字,可使用@ColumnInfo(name = “first_name”),Room爲每個字段在表中創建對應的字段;如果其中一些屬性不想被創建在表中怎麼辦,那就是使用 @Ignore 註解此屬性
  • **索引和唯一約束:**使用 @Entity 的 indices 來創建索引,並列出索引或者組合索引包含的列,可以使用unique屬性設置唯一約束Index(value = [“name”], unique = true)
@Entity(indices = [Index("name"), Index(value = ["first_name", "second_name"])])
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "second_name") val secondName: String?,
    @Ignore val desc: String?
)
  • **外鍵:**可以使用@ForeignKey 定義對象間的級聯操作
@Entity(foreignKeys = [ForeignKey(entity = User::class, parentColumns = ["uid"], childColumns = ["user_id"])])
data class Order(
    @PrimaryKey val orderNo: String,
    @ColumnInfo val address: String,
    @ColumnInfo(name = "user_id") val userId: Int
)
  • **嵌套對象:**可以使用@Embedded 嵌套其他實體對象,可以通過設置屬性 prefix 加前綴的方式保證字段名不重複。
@Embedded(prefix = "order") val order: Order?

DAO

定義一個User的數據訪問類

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user where first_name LIKE :first AND second_name LIKE :last LIMIT 1 ")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
    
    @Update
    fun updateUsers(vararg users: User)
}
  • **插入:**使用@Insert註解,Room將把所有的參數在一次事物中插入到數據庫中,Insert的參數onConflict用來指定當發生衝突是的策略。比如將@Index的unique屬性設置爲true,當產生衝突時,默認情況下爲OnConflictStrategy.ABORT會導致崩潰,這裏設置爲OnConflictStrategy.REPLACE,當發生衝突時替換老數據
  • **更新:**使用@Update註解方法,可以使用參數實體的值更新主鍵值和參數實體的主鍵相同的行
  • 刪除:@Delete註解方法,可以刪除主鍵值和參數實體的主鍵相同的行
  • 查詢:@Query支持查詢語句,刪除語句和更新語句,不支持插入語句。傳入參數使用“:參數名”來進行綁定。
    • 可觀察查****詢

在執行查詢時,想讓UI在數據更改時自動更新。可以在查詢方法使用 LiveData 類行的返回值。當數據更新時 Room 會自動生成所需的代碼已更新LiveData。

@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun getList(userIds: IntArray): LiveData<List<User>>
  • 響應查詢

可以從定義的查詢中返回 RxJava2 的 Publisher 和 Flowable 對象

@Query("SELECT * FROM user where first_name LIKE :first AND second_name LIKE :last LIMIT 1 ")
fun getByName(first: String, last: String): Flowable<User>

Database

@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

創建完完成後使用以下代碼獲取數據庫實例,並且使用單例來訪問

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

遷移數據庫

在APP升級時可能需要更改數據庫來策應新的功能。如果不希望數據庫中的數據丟失,
Room 允許我們編寫 Migration ,以此來遷移數據;如果使用fallbackToDestructiveMigration(),數據庫的內容都被清空。
每個遷移類制定一個開始版本和結束版本。在運行時,Room會運行每個Migration類的migrate()方法,並使用正確的順序將數據庫遷移到更高版本。
首先,需要更新Database的版本號

@Database(entities = arrayOf(User::class), version = 2)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

其次添加一個version:1->2的migration

val MIGRATION_1_2: Migration = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE User " + " ADD COLUMN mobile TEXT")
    }
}

最後把migration 添加到 databaseBuilder,addMigrations提供可選參數,支持多版本遷移,傳入多個migration。

var db = Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase::class.java, "database-name"
).allowMainThreadQueries()
    .addMigrations(MIGRATION_1_2).build()

Room的基本用法介紹已經END。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章