Jetpack - Room

一、概念

ORM框架:Object Relational Mapping(对象关系映射),将面向对象的编程语言和面向关系的数据库之间建立一种映射关系。

Entity数据库实体(数据模型)。对应一张表,表中的列根据实体类中的字段生成。
Dao数据库访问对象。必须是接口,根据业务封装对表操作的抽象方法会在编译时自动生成实现,调用时就不用跟底层语法打交道了。
Database数据库类。必须是抽象类,定义了版本号、包含哪些实体类,提供Dao层访问的实例。

二、使用

官方使用介绍

  • SQL中表和列名称不区分大小写;
  • Room必须拥有参数的访问权限(设为公开或提供getter&setter);

2.1 添加依赖

plugins {
    id 'kotlin-kapt'
}

dependencies {
    def room_version = "2.4.3"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    kapt "androidx.room:room-compiler:$room_version"    // To use Kotlin annotation processing tool (kapt)
}

2.2 创建 Entity 定义数据

数据库实体(数据模型)。对应一张表,表中的列根据实体类中的字段生成。

注解可选配置说明
@EntitytableName默认情况下会将类名用作表名,对类配置@Entity(tableName = "users")配置来更改。(注意SOL中表和列名称不区分大小写)
indices用来加快查询速度,对类配置@Entity(indices = [Index(calue = ["last_name","address"])])
primaryKeys需要通过多个列的组合进行唯一标识(定义复合键)可对类配置@Entity(primaryKeys = ["fristName","lastName"])
ignoredColumns如果实体继承了父类,对类配置@Entity(ignoredColumns = ["age"])来忽略父类中的字段。
@PrimaryKeyautoGenerate = true主键用于唯一标识每一行,对参数使用@primaryKey注解来设置,需要自增可配置autoGenerate = true。
@Ignore默认情况下会为每个参数创建一个列,对参数使用@Ignore注解来忽略。
@ColumnInfoname默认情况下会将参数名用作表的列名,一个参数对应一个列名,对参数使用@ColumnInfo来更改。(注意SOL中表和列名称不区分大小写)
typeAffinity该属性在表中的类型。

2.2.1 定义实体类

对类使用@Entity注解来设置该类为Room识别的实体类。默认情况下会将类名用作表名,对类配置@Entity(tableName = "users")配置来更改。(注意SOL中表和列名称不区分大小写)

@Entity(tableName = "users")    //不设置默认使用类名作为表名
data class User()

2.2.2 定义主键

主键用于唯一标识每一行,对参数使用@primaryKey注解来设置,需要自增可配置autoGenerate = true。需要通过多个列的组合进行唯一标识(定义复合键)可对类配置@Entity(primaryKeys = ["fristName","lastName"])

@Entity(primaryKeys = ["fristName","lastName"])    //定义复合键
data class User(
    @PrimaryKey(autoGenerate = true) val id: Long    //设为主键自增长
)

2.2.3 定义列名

默认情况下会将参数名用作表的列名,一个参数对应一个列名,对参数使用@ColumnInfo来更改。(注意SOL中表和列名称不区分大小写)

@Entity
data class User(
    @ColumnInfo(name = "frist_name") val fristName: String?    //不指定列名默认为参数名
)

2.2.4 忽略参数

默认情况下会为每个参数创建一个列,对参数使用@Ignore注解来忽略。如果实体继承了父类,对类配置@Entity(ignoredColumns = ["age"])来忽略父类中的字段。

open class Father {
    var age: Int? = null
}

@Entity(ignoredColumns = ["age"])    //忽略继承的参数
data class User() : Father() {
    @Ignore val price: Double?    //忽略该参数不会生成列,
}

@Entity
data class Person(
    @Ignore val name: String?    //如果放在主构造中
    var age: Int?    //其他字段不能为val,否则抛异常Cannot find setter for field.
) {
    constructor() : this(null)    //会需要提供一个次构造
}

2.2.5 支持全文搜索

需要通过全文搜索 (FTS) 快速访问数据库信息,对类使用@FTS3@FTS4,需要Room2.1.0以上版本。

@Fts4
@Entity
data class User()

2.2.6 将特定列编入索引 

用来加快查询速度,对类配置@Entity(indices = [Index(calue = ["last_name","address"])])

@Entity(indices = [Index(value = ["name","address"])])
data class User(
    val name: String?,
    val address: String?
)

2.3 创建 Dao 访问数据

数据库访问对象。必须是接口,根据业务封装对表操作的抽象方法会在编译时自动生成实现,调用时就不用跟底层语法打交道了。

注解说明函数参数使用Entity实例函数参数使用非Entity实例
@Insert插入可以直接使用注解只能使用@Query+SQL语句
@Delete删除
@Update更新
@Query查询@Query+SQL语句

2.3.1 @Insert @Delete @Update

传入的形参需要是Entity的实例。删除和更新是通过主键进行匹配,如果没有相同的行不会进行任何更改。

@Dao
interface UserDao {
    //插入
    @Insert
    fun insertUsers(vararg users: User)
    @Insert
    fun insertBothUsers(user1: User, user2: User)
    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)
    //删除
    @Delete
    fun deleteUsers(vararg users: User)
    //更新
    @Update
    fun updateUsers(vararg users: User)
}

2.3.2 @Query+SQL语句

编写SQL语句从而进行查询数据,或更复杂的插入、更新和删除。Room会在编译时自动验证查询语句,如果查询有问题会出现编译错误。

@Dao
interface UserDao {
    //使用非实体类参数,只能用@Query加上SQL语句
    @Query("delete from Person where age = :age")
    fun deleteByPersonName(age: Int): Int    //根据传入的年龄删除表中对应的数据
}

2.3.3 获取列的子集

有时候需要一次性获取某几列的数据,可以从查询返回简单对象,前提是可以将这些数据映射到返回的对象中。

//定义数据类,设置对应的字段
data class FullName(
    @ColumnInfo(name = "frist_name") val fristName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

//ROOM知道查询会返回frist_name,last_name这两个列的值,会被映射到FullName类的字段中
//如果查询返回的列未映射到返回对象的字段中,会显示警告
@Query("SELECT frist_name,last_name FROM Student")
fun getFullName(): List<FullName>    //查询语函数返回该简单对象

2.3.4 将参数传递给查询

通常需要接受传参以便进行过滤。

//单个参数
@Query("SELECT * FROM user WHERE age > :minAge")
fun getAllUsersOlderThan(minAge: Int): Array<User>
//多个参数
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
fun getAllUsersBetween(minAge: Int, maxAge: Int): Array<User>
//多次引用同一参数
@Query("SELECT * FROM user WHERE frist_name LIKE :str OR last_name LIKE :str")
fun getUsersWithName(str: String): List<User>
//一组参数
@Query("SELECT * FROM user WHERE region IN (:regions)")
fun getUsersFromRegions(regions: List<String>): List<User>

2.4 创建 Database 配置数据库

Database必须声明为抽象类。

注解可选配置说明
@Databaseverson数据库版本
entities包含的实体类
exportSchema = false是否支持导出
@Database(version = 1, entities = [User::class])  //指定数据库版本,与Entity进行关联
abstract class SemerWeatherDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao //与Dao进行关联

    companion object {  //单例因为全局只存在一份数据库
        private var instance: SemerWeatherDatabase? = null
        @Synchronized    //同步处理
        fun getInstance(context: Context): SemerWeatherDatabase = instance
        ?: Room.databaseBuilder(
                context.applicationContext,
                SemerWeatherDatabase::class.java,
                "SemerWeatherDB"    //数据库名称
            ).build()
    }
}

2.5 数据库升级

2.5.1 新增一张表

@Entity
data class Student(
    @PrimaryKey(autoGenerate = true) val id: Long
)

@Dao
interface StudentDao {
    @Delete
    fun deleteStudent(student: Student) //将传入的Student对象从表中删除
}

@Database(version = 2, entities = [User::class, Student::class])  //版本号升级到了2,后添加的Entity也要关联
abstract class SemerWeatherDatabase : RoomDatabase() {
    abstract fun UserDao(): UserDao
    abstract fun studentDao(): StudentDao  //后添加的Dao也要关联

    companion object {
        private var instance: SemerWeatherDatabase? = null

        val MIGRATION_1_2 = object : Migration(1, 2) {  //1升2的时候就要执行这个逻辑
            override fun migrate(database: SupportSQLiteDatabase) {
                //建表语句需要和后添加的Entity中声明的结构完全一致,否则报错
                database.execSQL("create table Student (id long primary key autoincrement not null)")
            }
        }

        @Synchronized
        fun getInstance(context: Context): SemerWeatherDatabase = instance
            ?: Room.databaseBuilder(
                context.applicationContext,
                SemerWeatherDatabase::class.java,
                "SemerWeatherDB"
            ).addMigrations(MIGRATION_1_2)  //对数据库进行分配置
                .build()
    }
}

2.5.2 原表新增列

@Entity
data class Student(
    @PrimaryKey(autoGenerate = true) val id: Long,
    val name: String
)

@Database(version = 3, entities = [User::class, Student::class])  //版本号升级到了3
abstract class SemerWeatherDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    abstract fun studentDao(): StudentDao

    companion object {
        private var instance: SemerWeatherDatabase? = null

        //新增
        val MIGRATION_2_3 = object : Migration(2, 3) {  //2升3的时候就要执行这个逻辑
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("alter table Student add column name text not null default 'unknown'")
            }
        }

        @Synchronized
        fun getInstance(context: Context): SemerWeatherDatabase = instance
            ?: Room.databaseBuilder(
                context.applicationContext,
                SemerWeatherDatabase::class.java,
                "SemerWeatherDB"
            ).addMigrations(MIGRATION_1_2, MIGRATION_2_3)  //对数据库进行分配置
                .build()
    }
}

2.6 代码中调用

class Repository {
    val db by lazy { PersonDatabase.getInstance(GlobalApplication.context) }
    val dao by lazy { db.personDao() }

    fun getData() {
        val id = dao.insertPerson(Person(null, "", 12))
        println("查ID:${db.personDao().queryPerson(id)}")
        val list = dao.getAllPerson()
        list.forEach { println("查所有:$it") }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于 Room 数据库的升级,您需要遵循以下步骤: 1. 在新版本的数据库中定义新表结构或对旧表结构进行更改,例如添加、删除或修改表的列。 2. 在您的 `AppDatabase` 类中增加数据库版本号,可以在类上使用 `@Database` 注解指定版本号,例如: ```kotlin @Database(entities = [User::class], version = 2) abstract class AppDatabase : RoomDatabase() { //... } ``` 3. 创建一个实现 `Migration` 接口的类,该类将包含从旧版本升级到新版本所需的所有更改。例如: ```kotlin val migration_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0") } } ``` 该示例代码表示,从版本 1 升级到版本 2,需要在 `users` 表中添加一个名为 `age` 的整数类型的列。 4. 在 `AppDatabase` 类中,使用 `addMigrations()` 方法将 `Migration` 对象添加到数据库中,例如: ```kotlin @Database(entities = [User::class], version = 2) abstract class AppDatabase : RoomDatabase() { //... companion object { val migration_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0") } } } //... init { if (BuildConfig.DEBUG) { // 在调试模式下,如果发现数据结构变化,将会清空数据 fallbackToDestructiveMigration() } else { // 在正式发布模式下,如果发现数据结构变化,将会执行升级脚本 addMigrations(migration_1_2) } } } ``` 在上述示例代码中,我们将 `migration_1_2` 对象添加到 `AppDatabase` 类的伴生对象中,并在 `init` 块中进行了初始化。我们还使用了 `fallbackToDestructiveMigration()` 方法,如果在调试模式下发现数据结构变化,将会清空数据。在正式发布模式下,我们使用了 `addMigrations()` 方法,将 `migration_1_2` 对象添加到数据库中,以执行升级脚本。 这样,在您的应用程序使用新版本的数据库时,将自动执行升级脚本,以将旧数据结构转换为新数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值