前两天我学习的是LifeCycle ViewModel LiveData 以及DataBinding 今天我要学习的是Room
- Room的介绍
- Room的使用
- Room的升级
Android官方ORM库Room
Android采用SQLite作为数据库存储,开源社区常见的ORM(Object Relational Mapping)库有ORMLite GreenDAO 等,Room与其他库一样都是在SQLite上提供一层封装。
Room重要的概念
Entity 实体类,对应的是数据库中的一张表结构,使用注解@Entity标记
Dao 包含访问一系列访问数据库的方法,使用注解@Dao标记
Database 数据库持有者,作为与应用持久化相关数据的底层连接的接入点。使用注解
@Database标记 ,另外需要满足以下条件 定义类必须是一个继承于RoomDatabase的抽象类,在注解中需要定义与数据库相关联的实体类列表。包含一个没有参数的抽象方法并且返回一个Dao对象。
这里我用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@Query("DELETe FROM student") fun deleteAllStudents() // 使用了LiveData 数据变动直接通知页面 @Query("SELECT * FROM student") fun getAllStudentsLive(): LiveData > }
创建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) { 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> { 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> { 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()
当更新失败
设置破坏式迁移,数据与结构全清空