Room简介
前言
本篇中数据库调试工具使用debug-db,没接触过的可以看这里介绍
https://blog.csdn.net/jinjin10086/article/details/103919983
Room综述
Room是Google Jetpack组件中的一员,是一种数据库的ORM框架,该库 在SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite
的强大功能的同时,获享更强健的数据库访问机制。
Room相关的类
注解相关
- @Entity注解:标识数据库映射关系的类,可以指定表名等,不指定的话默认表=名和类名一致,索引等信息。
- @PrimaryKey注解:指定主键,可设置自动增长等属性。
- @ColumnInfo注解:指定列信息,如列名等信息。
- @Ignore注解:标识忽略此属性,不对应生成数据库字段。
- @Dao注解:标识Dao层注解,编译时候会生成该接口或抽象类的实现。
- @Insert注解:标识该抽象方法为插入数据库,入参为Entity注解标注的类对象。
- @Query注解:标识该抽象方法为查询的方法(表面),需传入sql语句,其实sql语句可以实现增删改查的操作,也就是说可以实现常用全部操作。
- @Delete注解:标注该方法为删除的方法,入参为Entity注解标注的类对象。
- @Update注解:标注该方法为更新的方法,入参为Entity注解标注的类对象。
- @Database注解:标注该类为数据库操作类,一般为单例对象,入参entities为对应映射的kclass对象集合,verson处理数据库升级相关,通过此类可以拿到相关数据库操作的dao对象,进行相关的数据库操作。
类相关
- RoomDatabase.java类:抽象类,处理数据库初始化,及dao对象的生成相关操作,目前操作只需要接触到这个类即可。
- Room.java类:类,内部采用Builder模式,用于生成具体的RoomDatabase的子类对象
Room使用
简单使用
- 依赖引入,如下:
def room_version = “2.2.3”
implementation "androidx.room:room-runtime:$room_version”
kapt "androidx.room:room-compiler:$room_version”
- ORM映射类创建,如下
@Entity
class PersonBean constructor(){
constructor(name:String,age:Int):this(){
this.name = name
this.age = age
}
@PrimaryKey(autoGenerate = true)
var id:Int? = null
var name:String? = null
var age:Int? = null
@Ignore
var sex:Int?=null
override fun toString(): String {
return "PersonBean(id=$id, name=$name, age=$age, sex=$sex)”
}
}
- Dao创建(对应增删改查四个方法)
@Dao
abstract class PersonDao {
@Insert
abstract fun insert(person: PersonBean)
@Delete
abstract fun delete(person: PersonBean)
@Update
abstract fun update(person: PersonBean)
@Query("SELECT * FROM PersonBean”)
abstract fun getPersonList():MutableList
}
- RoomDatabase子类创建,持有dao的引用,代码如下:
@Database(entities = [PersonBean::class], version = 1)
abstract class PersonRoomDatabase : RoomDatabase() {
abstract fun personDao(): PersonDao
companion object {
@Volatile
private var instance: PersonRoomDatabase? = null
fun getPersonRoomDatabase(context:Context):PersonRoomDatabase{
if (null == instance){
synchronized(PersonRoomDatabase::class){
if (null == instance){
instance = Room.databaseBuilder(context.applicationContext,PersonRoomDatabase::class.java,"db_test").allowMainThreadQueries().build()
}
}
}
return instance!!
}
}
}
- 调用,插入,调用如下:
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_room)
roomTest()
}
private fun roomTest() {
val personDao = PersonRoomDatabase.getPersonRoomDatabase(this).personDao()
val person = PersonBean("张大爷”,80)
personDao.insert(person)
Log.d(Common.TAG,"插入数据:$person”)
val person1 = PersonBean("小张”,8)
personDao.insert(person1)
Log.d(Common.TAG,"插入数据:$person1”)
}
}
运行日志:
查询数据库数据如下,显然已经插入成功
数据库名称对应RoomDatabase子类中指定的数据库名称,表明对应映射对象的类名,数据和日志一直,插入了两条数据
- 调用,删除,调用如下(这里只需传入id即可,因为删除的时候只用到id):
val personBean = PersonBean()
personBean.id = 1
personDao.delete(personBean)
查询数据库如下,显然已经删除成功
7.调用,更新,调用如下:
val personBean = PersonBean("小张来了”,23)
personBean.id = 2
personDao.update(personBean)
查询数据如下,显然已经更新成功
- 调用,查询,如下:
var personList: MutableList = personDao.getPersonList()
Log.d(Common.TAG,"共有${personList.size}个人”)
personList.forEach {
Log.d(Common.TAG,it.toString())
}
日志如下:
查询数据如下,和日志一致:
扩展使用
@Query实现增删改
和之前的增删改效果一致,代码如下:
@Query("insert into PersonBean values (:id,:name,:age)”)
fun insertQuery(id:Int?,name:String?,age:Int?)
@Query("delete from PersonBean where id = :id”)
fun deleteQuery(id:Int?)
@Query("update PersonBean set name = :name , age = :age where id= :id”)
fun updateQuery(id:Int?,name:String?,age:Int?)
至于为什么没有使用person作为参数实现这个过程,在@Query的注释中已经说得很清楚了,只支持名字匹配识别
多参数插入、删除、更新操作
插入代码如下(更新和删除类似):
//dao
@Insert
fun insert(vararg person: PersonBean)
//调用
val person = PersonBean("张大爷", 80)
val person1 = PersonBean("张大妈", 79)
personDao.insert(person,person1)
Log.d(Common.TAG, "插入数据:$person”)
Log.d(Common.TAG, "插入数据:$person1”)
var personList: MutableList = personDao.getPersonList()
Log.d(Common.TAG, "共有${personList.size}个人”)
personList.forEach {
Log.d(Common.TAG, it.toString())
}
日志如下:
查询数据如下,和日志一致:
条件查询
基本数据如下:
查询Dao代码如下:
@Query("select * from PersonBean where id= :id”)
fun findById(id:Int?): MutableList
@Query("select * from PersonBean where age > :age”)
fun findByAge(age:Int?): MutableList
@Query("select * from PersonBean where name like :name”)
fun findByNameRule(name: String?): MutableList
1、findById方法id 传入3 ,结果如下:
2、findByAge方法 age传入16,结果如下:
3、findByNameRule方法
name传入”张%”
结果如下:
name传入”%张”
name传入"%张%”
结果如下:
Room升级
场景:
1、增删表、增删字段
步骤:
1、改变版本号,在RoomDatabase的子类,如下改变version即可:
@Database(entities = [PersonBean::class,Student::class], version = 2)
2、如果是增加表,则相应的增加、减少entities数组的内容
3、增加Migration,代码如下:
Room.databaseBuilder(context.applicationContext,PersonRoomDatabase::class.java,”db_test”)
.addMigrations(object: Migration(1,2){
override fun migrate(database: SupportSQLiteDatabase) {
//此处为增删表、增删字段的sql书写处
database.execSQL("CREATE TABLE IF NOT EXISTS `Student` (“ +
"`id` INTEGER PRIMARY KEY AUTOINCREMENT,” +
"`name` TEXT,” +
"`age` INTEGER “ +
");”)
}
})
.allowMainThreadQueries().build()
其实增加表的时候Sql可以在此处找到,目录如下
内容如下:
删除的语句也能找到,如下:
升级过程可以实现串式升级,即现在安装1版本,目标4版本,添加的迁移器是Migration(1,2),Migration(2,3),Migration(3,4),直接
升级是可以成功的,没有问题,不需要添加Migration(1,4),Migration(2,4)等迁移器。
关于Room使用,暂时先到此处,不做过多深入
实现过程
本来想分析一下创建表到数据操作的实现过程,限于篇幅与时间,后续再补上吧
注意
1、写Bean的时候一定要注意按照规范写 不然容易出现错误如下,没有具体报错的地方,其实是在编译阶段生成代码的时候报错了:
kotlin代码如下:
@Entity(tableName = “tb_person”)
class Person constructor(){
constructor(name:String,age:Int):this(){
this.name = name
this.age = age
}
@PrimaryKey(autoGenerate = true)
var id:Int? = null
var name:String? = null
var age:Int? = null
override fun toString(): String {
return "Person(id=$id, name=$name, age=$age)”
}
}
2、select的结果是列表,要注意,不写返回值编译时不能通过的。
3、数据库升级之后,再安装低版本会报错,不能覆盖安装,需要卸载之后重新安装,不然会报错如下:
提示高版本到低版本的合并操作没有添加合并器,记住:
卸载重新安装,卸载重新安装,卸载重新安装。
源码
源码链接:源码
总结
1、关于Jetpack Room的介绍先到此处,后续可能会继续深入。
2、自勉,实践出真知,blog记录自己成长,也能帮助一些需要该方面知识的人,不能没有经过验证的结论直接网上帖,那样难免会坑自己,
也可能坑别人,就像上述的Room数据库升级操作 ,串式升级验证过,卸载直接安装高版本也验证过,验证通过的,当然如果因为一些特殊因素
(如环境等可能会导致问题),那就再做讨论验证。
3、Room总的用起来总的感觉良好:
只需要在dao接口写方法声明就可以,通过PersonRoomDatabase拿到dao对象可以实现增删改查操作
插入、更新、删除都很简单,注解操作就能实现,当然你要写sql也没人拦着,支持多参数操作也比较方便。
查询操作是需要些sql的,参数支持名称识别,上述说过,总体也是很方便的
在数据库升级的时候感觉稍显麻烦,需要自己添加迁移操作,需要些sqk语句(如增删表、增删字段等),之前用litepal是不需要写这些的,
只需要更改版本号即可。
4、注意中写到的注意点都是我在使用时候踩到的坑记录下来,避免以后再踩,也给后来者一点提示,能少踩点坑。