迁移到 Android Room

说明

Android Room 作为 Android Architecture 的 orm 部分,接入是非常简单的。

首先明确 Room 能做什么,简单概括,能让你把一行 SQL 语句变成对象,是现在最好用的 ORM 框架,当然这是废话,官方能拿的出来,肯定要比第三方的要好。首先体验一下:

简单的插入数据,不用写 SQL:

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg record: Record)

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(record: Record)
复制代码

查询数据,一行 SQL,一个函数声明搞定

@Query("SELECT * FROM Record")
fun getAll(): List<Record>
复制代码

而且 SQL 是有代码提示语法检查的。

下面,我们在现有项目上使用 Room,使用新的orm框架,而不用对数据库结构做任何修改。

添加 Room

def room_version = "1.1.0" // or, for latest rc, use "1.1.1-rc1"

implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
复制代码

Entity

现在的项目数据量有个 Record table,创建的 SQL 语句为

CREATE TABLE Record (
    _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    date INTEGER UNIQUE NOT NULL,
    records TEXT,
    need_sync INTEGER DEFAULT 0
);
复制代码

主键为 _iddata 为整型,存储的是时间,records 为字符串,是一个 json 对象,need_sync 是一个 Boolean,以整型存储在数据库中。

我想做的:

  • _id 在数据类中要名字是 id
  • date 为 UNIQUE
  • date 直接读取为 LocalDate 对象
  • records 直接读取为 Bean 对象
  • need_sync 直接读取为 Boolean,数据类中的名字当然也要改

最终的 Entity 类声明为:

@Entity(tableName = "Record", indices = [(Index(value = arrayOf("date"), unique = true))])
class Record {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    var id: Long = 0

    var date: LocalDate? = null
    var records: Bean? = null

    @ColumnInfo(name = "need_sync")
    var needSync = false
}
复制代码

除了类声明,我们不需要 SQL 语句去创建 Table

Dao

RecordDao,用来访问数据对象:

@Dao
interface RecordDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(vararg record: Record)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(record: Record)

    @Delete
    fun delete(record: Record)

    @Delete
    fun deleteAll(vararg record: Record)

    @Update
    fun update(record: Record)

    @Query("SELECT * FROM Record")
    fun getAll(): List<Record>

    @Query("SELECT * FROM Record WHERE date = :date")
    fun getByDate(date: LocalDate): List<Record>
}
复制代码

直接的插入、删除、更新是不用写 SQL,自定义的查询还是需要写 SQL,可以直接在 SQL 语句中绑定函数参数,只要在参数名字前加 “:” 就可以了,像 getByDate:date 就是标识函数的 date 参数就是 SQL 中的参数:

@Query("SELECT * FROM Record WHERE date = :date")
fun getByDate(date: LocalDate): List<Record>
复制代码

@Query 里面的 SQL 语句不仅有代码提示,而且有语法检查,写错 table name 和 函数参数名都会报错的,手残党的福音啊。

Database

Entity 和 Dao 都声明好了,现在要创建数据库了,还记得之前怎么做的吗:

  • 继承 SQLiteOpenHelper
  • onCreate 执行 SQL 语句创建数据库
  • onUpgrade 进行 Migration
  • execSQL 获取 Cursor
  • Cursor 转换为对象

如果使用 Room 呢?

没有 SQLiteOpenHelper 了,也没有 onCreate 了:

@Database(entities = [(Record::class)], version = 10)
abstract class AppDataBase : RoomDatabase() {

    abstract fun recordDao(): RecordDao
}
复制代码

数据库创建的声明完成了,使用了 Room,要接受这几点:

  • 不需要 SQLiteOpenHelper
  • 不需要 Cursor
  • 可能连 SQLiteDatabase 也不需要

TypeConverters

上面是创建数据库的全部代码了吗?当然不是,我们还需要 TypeConverters 和 Migration,

date 在数据里面以 Long 的形式存储,读取的时候需要转换为 LocalDate 对象,存储的时候需要把 LocalDate 转换为 Long,对于一种转换关系,我们只需要声明一个静态函数就可以了,名字随意,参数和返回值的类型为对应的转换关系。

class DbTypeConverters {
    companion object {

        @TypeConverter
        @JvmStatic
        fun toLocalDate(value: Long): LocalDate =
                LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneId.systemDefault()).toLocalDate()

        @TypeConverter
        @JvmStatic
        fun toLocalDate(value: LocalDate): Long =
                value.atStartOfDay(ZoneId.of("UTC")).toInstant().toEpochMilli()
    }

}
复制代码

然后把 TypeConverters 添加到 Database

@Database(entities = [(Record::class)], version = 10)
@TypeConverters(DbTypeConverters::class)
abstract class AppDataBase : RoomDatabase() {

    abstract fun recordDao(): RecordDao
}
复制代码

Migrations

数据库升级,我们需要声明的是一个 Migration,

object PeriodDbMigration {

    @JvmField
    val MIGRATION_1_2 = object : Migration(1, 2) {

        override fun migrate(database: SupportSQLiteDatabase) {
            // update
        }

    }

    @JvmField
    val MIGRATION_3_4 = object : Migration(3, 4) {

        override fun migrate(db: SupportSQLiteDatabase) {
            // update
        }

    }
}
复制代码

和 SQLiteOpenHelper 的 onUpgrade 一样,只不过每次升级拆分为一次 Migration 操作。

然后就是创建数据库:

通过 addMigrations 添加数据库升级的操作,

val database = Room.databaseBuilder(application, PeriodDataBase.class, "database.db")
                .addMigrations(PeriodDbMigration.MIGRATION_1_2, PeriodDbMigration.MIGRATION_3_4)
                .allowMainThreadQueries()
                .build();
复制代码

allowMainThreadQueries() 是允许在主线程上进行操作,对于之前在主线程上读写数据的同学们,先加上 allowMainThreadQueries(),然后慢慢优化吧。

使用

一般会把数据作为单例使用,然后调用 Dao 中函数就可以了:

val all = database.recordDao().getAll()
复制代码

转载于:https://juejin.im/post/5b20f580f265da6e2a08c1d2

Android RoomAndroid平台上的一个持久化库,用于处理本地数据库的操作。当在应用程序中使用Room时,可能会遇到需要进行数据迁移的情况,例如更改数据库模式或添加新的表。 在Room中进行数据迁移可以通过数据库升级来实现。下面是一些常见的步骤: 1. 在Room的Database类中,使用`@Database`注解来定义你的数据库,并指定`version`属性,表示当前数据库的版本号。 ```kotlin @Database(entities = arrayOf(User::class), version = 2) abstract class AppDatabase : RoomDatabase() { // ... } ``` 2. 创建一个新的数据库版本,并在新版本中进行必要的更改。例如,你可以添加新的实体类或更改现有实体类的结构。 ```kotlin @Database(entities = arrayOf(User::class, Order::class), version = 3) abstract class AppDatabase : RoomDatabase() { // ... } ``` 3. 创建一个用于执行数据库升级的`Migration`对象。`Migration`对象需要实现`Migration`接口,并在`migrate()`方法中定义旧版本到新版本的迁移逻辑。 ```kotlin val MIGRATION_2_3: Migration = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { // 执行数据库迁移操作 } } ``` 4. 在创建Room的`Database`对象时,使用`.addMigrations()`方法将迁移对象添加到构建器中。 ```kotlin val db = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "app_database" ) .addMigrations(MIGRATION_2_3) .build() ``` 这样,在应用程序升级时,Room将会自动检测到数据库版本的变化,并执行相应的迁移操作。 需要注意的是,当进行数据库迁移
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值