前两天我学习的是LifeCycle ViewModel LiveData 以及DataBinding 今天我要学习的是Room
Room的介绍
Android官方ORM库Room
Android采用SQLite作为数据库存储,开源社区常见的ORM(Object Relational Mapping)库有ORMLite GreenDAO 等,Room与其他库一样都是在SQLite上提供一层封装。
Room重要的概念
Entity 实体类,对应的是数据库中的一张表结构,使用注解@Entity标记
Dao 包含访问一系列访问数据库的方法,使用注解@Dao标记
Database 数据库持有者,作为与应用持久化相关数据的底层连接的接入点。使用注解
@Database标记 ,另外需要满足以下条件 定义类必须是一个继承于RoomDatabase的抽象类,在注解中需要定义与数据库相关联的实体类列表。包含一个没有参数的抽象方法并且返回一个Dao对象。
Room的使用
这里我用Kotlin 代码做案例,最后附加上java 代码。
引入依赖库
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "kotlin-kapt"
implementation "androidx.room:room-runtime:2.2.6"
kapt "androidx.room:room-compiler:2.2.6"
implementation "androidx.room:room-ktx:2.2.6"
Room 三大部分概述
Room组要由三大部件组成:@Entity,@Dao和@Database。
简单来讲,@Entity是用来创建具体数据表的,其以数据model类的形式,展现一个数据表中的各个属性。
@Dao是个接口类,一般提供某张表的各种增删改查的方法
@Database作为数据库的根基,数据库文件的位置,数据库的创建都是在此类中完成。@Database注解的类一般会提供一个单例对象,即数据库操作对象,然后会有各个数据表的注册(或声明),以及这些数据表对象的获取的抽象方法放到这个类中。@Database注解的类是一个抽象类。
创建Entity
Kotlin中创建Entity跟Java 差不多,也就是创建一个数据模型给Room去使用,有两种写法 一种是Data 写法 ,还有一种Class 格式,因我我也是最近刚学完的Kotlin 很多地方不是很熟练,比较倾向于 Class格式的写法
@Entity(tableName = "student")
class Student() {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
var name: String = ""
var age: Int = 0
constructor(name: String, age: Int) : this() {
this.name = name
this.age = age
}
constructor(id: Int) : this() {
this.id = id
}
constructor(id: Int, name: String, age: Int) : this() {
this.id = id
this.name = name
this.age = age
}
}
创建Dao
@Dao
interface StudentDao {
@Insert
fun insertStudent(vararg student: Student)
@Delete
fun deleteStudent(vararg students: Student)
@Update
fun updateStudent(vararg students: Student)
@Query("SELECT * FROM student")
fun getAllStudent(): List<Student>
@Query("DELETE FROM student")
fun deleteAllStudents()
// 使用了LiveData 数据变动直接通知页面
@Query("SELECT * FROM student")
fun getAllStudentsLive(): LiveData<List<Student>>
}
创建Database
这是我自己的写法,因为Kotlin 不是很熟练,可能有些地方用的不是很恰当,以后随着我Kotlin的技能不断提升,我会替换的。
@Database(version = 1, exportSchema = false, entities = [Student::class])
abstract class MyDatabase : RoomDatabase() {
companion object {
private val DATABASE_NAME = "my_db.db"
@Volatile
private var mInstance: MyDatabase? = null
@Synchronized
fun getInstance(context: Context): MyDatabase {
mInstance?.let {
return it
}?:let {
mInstance = Room.databaseBuilder(
context.applicationContext,
MyDatabase::class.java,
DATABASE_NAME
).fallbackToDestructiveMigration()
.build()
return mInstance as MyDatabase
}
}
}
abstract fun getStudentDao(): StudentDa
}
Repository类(与Room 无关)
这里我为了方便操作,写了一个Repository类,放在子线程对数据库进行操作,不知道写的是否合理,也希望能遇到大佬帮忙看一下。
class StudentRepository(context: Context) {
/**
* 数据处理类, 对数据库的操作属于耗时操作 我这边放在了子线程中处理
* 使用LiveData 随时变更数据
**/
var studentDao: StudentDao
init {
val instance = MyDatabase.getInstance(context)
studentDao = instance.getStudentDao()
}
suspend fun insertStudent(students: Student) = withContext(Dispatchers.IO) {
studentDao.insertStudent(students)
}
suspend fun deleteStudent(students: Student) = withContext(Dispatchers.IO) {
studentDao.deleteStudent(students)
}
suspend fun deleteAllStudents() = withContext(Dispatchers.IO) {
studentDao.deleteAllStudents()
}
suspend fun updateStudent(students: Student) = withContext(Dispatchers.IO) {
studentDao.updateStudent(students)
}
fun getAllStudentsLive(): LiveData<List<Student>> {
return studentDao.getAllStudentsLive()
}
}
viewModel类 (与Room 无关)
class StudentViewModel(application: Application) : AndroidViewModel(application) {
var repository: StudentRepository = StudentRepository(application)
//启用协程处理
fun insertStudent(students: Student) {
viewModelScope.launch {
repository.insertStudent(students)
}
}
fun updateStudent(students: Student) {
viewModelScope.launch {
repository.updateStudent(students)
}
}
fun deleteStudent(students: Student) {
viewModelScope.launch {
repository.deleteStudent(students)
}
}
fun deleteAllStudents() {
viewModelScope.launch {
repository.deleteAllStudents()
}
}
fun getAllStudentsLive(): LiveData<List<Student>> {
return repository.getAllStudentsLive()
}
}
这些就是数据库进行增删改查的操作
Room的升级
例如新增一个Sex字段
@Entity(tableName = "student")
class Student() {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
var name: String = ""
var age: Int = 0
var sex: Int = 0
constructor(name: String, age: Int,sex:Int) : this() {
this.name = name
this.age = age
this.sex = sex
}
constructor(id: Int) : this() {
this.id = id
}
constructor(id: Int, name: String, age: Int) : this() {
this.id = id
this.name = name
this.age = age
}
}
在Database中需要修改
@Database(version = 1, exportSchema = false, entities = [Student::class])
abstract class MyDatabase : RoomDatabase() {
companion object {
private val DATABASE_NAME = "my_db.db"
@Volatile
private var mInstance: MyDatabase? = null
@Synchronized
fun getInstance(context: Context): MyDatabase {
mInstance?.let {
return it
} ?: let {
mInstance = Room.databaseBuilder(
context.applicationContext,
MyDatabase::class.java,
DATABASE_NAME
).addMigrations(migration1to2)
.fallbackToDestructiveMigration()
.build()
return mInstance as MyDatabase
}
}
// 增加的版本更新
private val migration1to2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE student ADD COLUMN 'sex' INTEGER NOT NULL")
}
}
}
abstract fun getStudentDao(): StudentDao
}
fallbackToDestructiveMigration()
当更新失败
设置破坏式迁移,数据与结构全清空