简介

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。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐