JetPack----Room(二)

175 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

JetPack----Room(二)

上文讲解到了数据库映射实体表和数据访问对象(Dao)的定义,接下来我们继续讲解DataBase的创建

//实体表类
@Database(
    entities = [GoodsEntity::class, ClassificationEntity::class, OrderEntity::class, StaffEntity::class, AppConfigEntity::class],
    version = 1, exportSchema = false
)
//复杂数据类型的转换类
@TypeConverters(
    value = [DateTypeConverters::class]
)
abstract class AppDatabase : RoomDatabase() {
    //Dao的访问实例
    abstract fun goodsDao(): GoodsDao
    abstract fun classificationDao(): ClassificationDao

    companion object {

        @Volatile
        private var instance: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            return instance ?: synchronized(this) {
                instance ?: buildDatabase(context).also { instance = it }
            }
        }

        private fun buildDatabase(context: Context): AppDatabase {

            return Room.databaseBuilder(context, AppDatabase::class.java, "db_name")
                .addCallback(
                    object : AppDatabase.Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                        }
                    }
                )
                .build()
        }
    }
}

可以看到我们在AppDataBase类上有个Database注解,并且在注解中声明了实体类以及数据库版本号。我们的DataBase必须时继承RoomDataBase,并且一定要使用abstract关键字将它声明为抽象类,然后提供相应的抽象方法来提供数据访问对象Dao的实例,然后我们在伴生类中编写了一个单例用来提供给外部获取database,从而使用Dao实例来进行数据操作。Room虽然需要经常编写SQL,但是Room支持在编译期间帮我们动态检查SQL语法。

在我们进行数据查询时我们不妨会涉及到多变查询,这个时候有过数据库基础的通过都会知道我们需要建立表间关系来进行多表查询,这个时候我们可以通过纯 SQL的方法来进行数据查询,还有一种就是通过Room为我们提供的方法,下面我们就看下Room的多表查询的用法

纯Sql的多表查询

@Dao
interface UserBookDao {
    @Query(
        "SELECT user.name AS userName, book.name AS bookName " +
        "FROM user, book " +
        "WHERE user.id = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

Room提供的多表查询方式

//商品表
@Entity(tableName = "table_goods")
data class GoodsEntity(
    @PrimaryKey(autoGenerate = true) var id: Long,
    /**
     * 商品Guid
     * */
    @ColumnInfo(name = "guid") var guid: String,
    /**
     * 商品名称
     * */
    @ColumnInfo(name = "name") var name: String,

    /**
     * 商品单位
     * */
    @ColumnInfo(name = "unit") var unit: String,
    /**
     * 商品销售价
     * */
    @ColumnInfo(name = "sale_price") var salePrice: String,
    /**
     * 商品分类id
     * 关联商品分类表(table_classification)
     * */
    @ColumnInfo(name = "classification_guid") var classificationGuid: String,
)
//分类表
@Entity(tableName = "table_classification")
data class ClassificationEntity(
    @PrimaryKey(autoGenerate = true) var id:Long,
    /**
     * 分类Guid
     * */
    @ColumnInfo(name = "guid") var guid:String,
    /**
     * 分类名称
     * */
    @ColumnInfo(name = "name") var name:String,
)

这时我们有个需求就是根据分类显示商品这个时候我们就需要查询商品同时也需要根据分类Id查询分类名称,我们需要在两个实体之间建立对应关系,所以需要创建一个新的数据类,其中每个实例都包含父实体的一个父实体的一个实例和与之对应的子实体实例。我们需要将@Relation注释添加到子实体上,同时将paretColumn设置为父实体主键列的名称,同时将entityColum设置为引用父实体逐渐的子列名称,@Embedded注释表示要分解为表格中的子字段对象

data class GoodsAndClassification(

    @Embedded
    val classification: ClassificationEntity,
    @Relation(
        parentColumn = "guid",
        entityColumn = "classification_guid"
    )
    val goods: List<GoodsEntity>?
)

Dao的编写,因为该方法会将父实体与子实体配对的成功的数据类的所有实例返回,所以Room需要执行两次查询,所以我们需要加上@Transaction确保整个操作的原子性

  /**
     * 查询商品以及分类名称
     * 商品表和分类表设计是一对一关系
     **/
    @Transaction
    @Query("SELECT * FROM table_classification")
    fun getGoodsAndClassification() : List<GoodsAndClassification>

Room的升级

我们的数据库结构不可能永远是一层不表的,随着版本的变更,数据库也是需要升级的。我们不可能每次版本更新就要求用户删除数据,那样用户的数据丢失,对我们来说也是得不偿失,所以数据的升级也是非常重要。Room在数据库的升级上设计的非常繁琐,没有比原来的SQLiteDataBase好到那里去

  • 在我们AppDataBase伴生对象中构建数据库是我们可以通过addMigrations(Migration migrations)方法进行版本升级,可以实现多个版本的升级
  • Migration(int startVersion,int endVersion)方法是指定从什么版本升级到哪一版本,每次迁移都可以定义在两个版本之间移动
  • 在重写的migrate()方法中执行更新表结构的Sql语句
比如我们在商品表中新增一个成本价字段
private val MIGRATION_1_2 = object : Migration(1,2){
    override fun migrate(database: SupportSQLiteDatabase) {
        //商品表  新增一列
        database.execSQL("ALTER TABLE table_goods add COLUMN cost_price text")
    }
}
private fun buildDatabase(context: Context): AppDatabase {

            return Room.databaseBuilder(context, AppDatabase::class.java, "db_name")
                .addCallback(
                    object : AppDatabase.Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                        }
                    }
                )
     		   .addMigrations(MIGRATION_1_2)
                .build()
        }