.build()
Demo效果.png
通过 Database Inspector 工具可以看到 DB 数据创建成功了。Database Inspector 支持实时刷新,查询和修改等 DB 操作,是 DB 开发的利器。
如果不知道如何使用Database Inspector,可参考官方文档使用 Database Inspector 调试数据库
@Insert 支持设置冲突策略,默认为 OnConflictStrategy.ABORT 即中止并回滚。还可以指定为其他策略。
OnConflictStrategy.REPLACE 冲突时替换为新记录
OnConflictStrategy.IGNORE 忽略冲突 (不推荐)
OnConflictStrategy.ROLLBACK 已废弃,使用 ABORT 替代
OnConflictStrategy.FAIL 同上
其声明的方法返回值可为空,也可为插入行的 ID 或列表。
fun insertWithOutId(movie: Movie?)
fun insert(movie: Movie?): Long?
fun insert(vararg movies: Movie?): LongArray?
@Delete
和 @Insert 一样支持不返回删除结果或返回删除的函数,不再赘述。
@ Update
和 @Insert 一样支持设置冲突策略和定制返回更新结果。此外需要注意的是 @Update 操作将匹配参数的主键 id 去更新字段。
fun update(vararg movies: Movie?): Int
@ Query
查询操作主要依赖 @Update 的 value,指定不同的 SQL 语句即可获得相应的查询结果。在编译阶段就将验证语句是否正确,避免错误的查询语句影响到运行阶段。
查询所有字段
@get:Query(“SELECT * FROM movie”)
查询指定字段
@get:Query(“SELECT id, movie_name, actor_name, post_year, review_score FROM movie”)
排序查询
@get:Query(“SELECT * FROM movie ORDER BY post_year DESC”) 比如查询最近发行的电影列表
匹配查询
@Query(“SELECT * FROM movie WHERE id = :id”)
多字段匹配查询
@Query(“SELECT * FROM movie WHERE movie_name LIKE :keyWord " + " OR actor_name LIKE :keyWord”) 比如查询名称和演员中匹配关键字的电影
模糊查询
@Query(“SELECT * FROM movie WHERE movie_name LIKE ‘%’ || :keyWord || ‘%’ " + " OR actor_name LIKE ‘%’ || :keyWord || ‘%’”) 比如查询名称和演员中包含关键字的电影
限制行数查询
@Query(“SELECT * FROM movie WHERE movie_name LIKE :keyWord LIMIT 3”) 比如查询名称匹配关键字的前三部电影
参数引用查询
@Query(“SELECT * FROM movie WHERE review_score >= :minScore”) 比如查询评分大于指定分数的电影
多参数查询
@Query(“SELECT * FROM movie WHERE post_year BETWEEN :minYear AND :maxYear”) 比如查询介于发行年份区间的电影
不定参数查询
@Query(“SELECT * FROM movie WHERE movie_name IN (:keyWords)”)
Cursor 查询
@Query(“SELECT * FROM movie WHERE movie_name LIKE ‘%’ || :keyWord || ‘%’ LIMIT :limit”)
fun searchMoveCursorByLimit(keyWord: String?, limit: Int): Cursor?
注意: Cursor 需要保证查询到的字段和取值一一对应,所以不推荐使用
响应式查询
demo 采用的 LiveData 进行的观察式查询,还可以配合 RxJava2,Kotlin 的 Flow 进行响应式查询。
数据库升级降级
在 Movie 类里增加了新字段后,重新运行已创建过 DB 的 demo 会发生崩溃。
private fun createInstance(context: Context): MovieDataBase {
return Room.databaseBuilder(context.applicationContext, MovieDataBase::class.java, DATA_BASE_NAME)
.fallbackToDestructiveMigration()
.addCallback(object : Callback() {
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
super.onDestructiveMigration(db)
// Init DB again after db removed.
Executors.newFixedThreadPool(5).execute {
val dataBase = getInstance(context)
val ids = dataBase!!.movieDao().insert(*Utils.initData)
dataBase.databaseCreated.postValue(true)
.build()
private fun createInstance(context: Context): MovieDataBase {
return Room.databaseBuilder(context.applicationContext, MovieDataBase::class.java, DATA_BASE_NAME)
// .fallbackToDestructiveMigration()
.addMigrations(object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE movie "
+ " ADD COLUMN review_score INTEGER NOT NULL DEFAULT 8.0")
.build()
降级则调用:
fallbackToDestructiveMigrationOnDowngrade() 来指定在降级时删除 DB,也可以像上述那样指定 drop column 来进行数据迁移。
如果想要迁移数据,无论是升级还是降级,必须要给 @Database 的 version 指定正确的目标版本。Migration 迁移处理的起始版本以及实际的迁移处理 migrate() 都必不可少。
当我们的 DB 操作需要保持一致性,或者查询关联性结果的时候需要保证事务处理。Room 提供了 @Transaction 注解帮助我们快速实现这个需求,它将确保注解内的方法运行在同一个事务模式。
public interface MovieDao {
@Transaction
default void insetNewAndDeleteOld(Movie newMovie, Movie oldMovie) {
insert(newMovie);
delete(oldMovie);
public long[] insert(final Movie... movies) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
long[] _result = __insertionAdapterOfMovie.insertAndReturnIdsArray(movies);
__db.setTransactionSuccessful();
return _result;
} finally {
__db.endTransaction();
db.runInTransaction(Runnable {
val database = db.getOpenHelper().getWritableDatabase();
val contentValues = ContentValues()
contentValues.put("movie_name", newMovie.getName())
contentValues.put("actor_name", newMovie.getActor())
contentValues.put("post_year", newMovie.getYear())
contentValues.put("review_score", newMovie.getScore())
database.insert("movie", SQLiteDatabase.CONFLICT_ABORT, contentValues)
database.delete("movie", "id = " + oldMovie.getId(), null)
SupportSQLiteDatabase 的创建
SupportSQLiteDatabase 是模仿 SQLiteDatabase 作成的接口,供 Room 框架内部对 DB 进行操作。由 FrameworkSQLiteDatabase 实现,其将通过内部持有的 SQLiteDatabase 实例,代理 DB 操作。
SupportSQLiteDatabase 的创建由增删改查等 DB 操作触发,需要经历 DB 的创建,表的创建,表的初始化,升降级以及打开等过程。
创建 DB 文件
RoomDatabase 的实例建议采用单例模式管理
不要在 UI 线程执行 DB 操作,否则发生异常:
Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
通过调用:
allowMainThreadQueries() 可以回避,但不推荐。
不要在 Callback#onCreate() 里同步执行 insert 等 DB 处理,否则将阻塞 DB 实例的初始化并发生异常: getDatabase called recursively。
@Entity 注解类不要提供多个构造函数,使用 @Ignore 可以回避。
Callback#onCreate() 并非由 RoomDatabase$Builder#build() 触发,而是由具体的增删改查操作触发,切记。
通过上述的实战和原理介绍可以看出,Room 的本质是在 SQLite 的基础上进行封装的抽象层,通过一系列注解让用户能够更简便的使用 SQLite。正因为此,它具备了一些优势,值得开发者大胆使用。
声明注解便能完成接口的定义,易上手
编译阶段将验证注解里声明的 SQL 语句,提高了开发效率
支持使用 RxJava2,LiveData 以及 Flow 进行异步查询
相较其他数据库框架 SQL 执行效率更高
Demo参见Github-JetpackDemo
Android官方文档
使用Android Jetpack 的 Room将数据保存到本地数据库。
Android官方示例