说明
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复制代码
而且 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);复制代码
主键为 _id
,data
为整型,存储的是时间,records
为字符串,是一个 json 对象,need_sync
是一个 Boolean,以整型存储在数据库中。
我想做的:
_id
在数据类中要名字是id
date
为 UNIQUEdate
直接读取为 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,用来访问数据对象:
@Daointerface 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@Query("SELECT * FROM Record WHERE date = :date") fun getByDate(date: LocalDate): List }复制代码
直接的插入、删除、更新是不用写 SQL,自定义的查询还是需要写 SQL,可以直接在 SQL 语句中绑定函数参数,只要在参数名字前加 “:” 就可以了,像 getByDate
,:date
就是标识函数的 date 参数就是 SQL 中的参数:
@Query("SELECT * FROM Record WHERE date = :date")fun getByDate(date: LocalDate): List复制代码
@Query
里面的 SQL 语句不仅有代码提示,而且有语法检查,写错 table name 和 函数参数名都会报错的,手残党的福音啊。
Database
Entity 和 Dao 都声明好了,现在要创建数据库了,还记得之前怎么做的吗:
- 继承 SQLiteOpenHelper
onCreate
执行 SQL 语句创建数据库onUpgrade
进行 MigrationexecSQL
获取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()复制代码