簡介
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。