Android采用sqlite作为数据库存储,Room就是Google推出的自己的ORM(Object Relational Mapping)。
Room的架构图:
- Entity:一个Entity对应于数据库中的一张表。Entity类是Sqlite表结构对Java类的映射,在Java中可以被看作一个Model类。
- Dao:即Data Access Objects,数据访问对象,可以通过它来访问数据。
一个Entity代表一张表,而每张表都需要一个Dao对象,用于对表进行增/删/改/查。Room数据库在被实例化之后,可以通过数据实例得到Dao对象(Get Dao),进而通过Dao对象对表中的数据进行操作。
使用Room
- 在app的build.gradle中添加Room的相关依赖
implementation 'androidx.room:room-runtime:2.2.5'
kapt "androidx.room:room-compiler:2.2.5"
- 创建一个Entity,即一张表。新建一个类文件,并在类文件的上方添加@Entity标签。
@Entity(tableName = "student")
data class Student(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
var id: Int?,
@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
var name: String?,
@ColumnInfo(name = "age", typeAffinity = ColumnInfo.TEXT)
var age: String?
)
- @Entity标签用于将Student类与Room中的数据表对应起来。tableName属性可以位数据表设置表名,若不设置,则表名与类名相同。
- @PrimaryKey标签用于指定该字段作为表的主键
- @ColumnInfo标签可用于设置该字段存储在数据库表中的名字,并指定字段类型。
- @Ignore标签用来告诉Room忽略该字段或方法。
- 针对上面的Entity,需要定义一个Dao接口文件,以便于对Entity进行访问。需要在文件上方添加@Dao标签。
@Dao
interface StudentDao {
@Insert
fun insertStudent(student: Student)
@Delete
fun deleteStudent(student: Student)
@Update
fun updateStudent(student: Student)
@Query("SELECT * FROM student")
fun getStudentList(): LiveData<List<Student>>
@Query("SELECT * FROM student WHERE id = :id")
fun getStudentById(id: Int)
}
- 定义好Entity和Dao后,接下来创建数据库。
const val MY_DATABASE_NAME = "my_db"
@Database(entities = [Student::class], version = 1)
abstract class MyDatabase : RoomDatabase() {
companion object {
val instance = Single.sin
}
private object Single {
val sin: MyDatabase =
Room.databaseBuilder(MyApplication.instance(), MyDatabase::class.java, MY_DATABASE_NAME)
.build()
}
abstract fun studentDao(): StudentDao
}
@Database标签用于告诉系统这是Room数据库对象。entities属性用于指定该数据库有哪些表,若需要建立多张表,则表名以逗号相隔开;version属性用于指定数据库版本号,数据库升级依据版本号进行判断。
数据库类需要继承自RoomDatabase,并通过Room.databaseBuilder()结合单例设计模式完成创建。
- 数据库和表创建完成后,就可以通过单例模式调用数据库进行增/删/改/查,接口调用时需要注意线程切换。
与LiveData、ViewModel结合使用
当Room数据库中的数据发生变化时,通过LiveData组件通知View层,实现数据的自动更新。
class StudentViewModel(application: MyApplication): AndroidViewModel(application) {
private val myDatabase = MyDatabase.instance
val liveDataStudent = myDatabase.studentDao().getStudentList()
}
private val studentViewModel: StudentViewModel by viewModels()
studentViewModel.liveDataStudent.observe(viewLifecycleOwner, {
students -> { TODO("显示students列表")}
})
数据库升级
Android提供了一个名为Migration的类来对Room进行升级。
public Migration(int startVersion, int endVersion) {
this.startVersion = startVersion;
this.endVersion = endVersion;
}
Migration有两个参数,startVersion和endVersion,startVersion表示当前数据库版本,endVersion表示将要升级到的版本。
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
TODO("执行升级相关操作")
}
}
在Migration中编写完升级方案后,需要通过addMigrations()方法,将升级方案添加到Room。
Room.databaseBuilder(MyApplication.instance(), MyDatabase::class.java, MY_DATABASE_NAME)
.addMigrations(MIGRATION_1_2)
.build()
异常处理
Room在升级过程中没有匹配到相应的Migration,会导致应用程序崩溃,可以在创建数据库时加入fallbackToDestructiveMigration()方法,此方法能在升级异常时,重新创建数据表。但需要注意,虽然应用程序不会崩溃,但所有数据会丢失。
Room.databaseBuilder(MyApplication.instance(), MyDatabase::class.java, MY_DATABASE_NAME)
.addMigrations(MIGRATION_1_2)
.fallbackToDestructiveMigration()
.build()
Schema文件
Room提供了一项功能,在每次数据库升级的过程中,会导出一个Schema文件,这是一个json格式的文件,其中包含了数据库的所有基本信息。
可以在app的build.gradle文件中指定schema文件的导出位置。
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":"$projectDir/ schemas".toString()]
}
}
Room默认导出Schema文件,如果不想导出,可以在数据库标签@Database中指定exportSchema=false
销毁与重建策略
- 创建一张符合表结构要求的临时表temp
- 将数据从旧表复制到临时表temp
- 删除旧表
- 将临时表temp重命名为原表名
预填充数据
如果已有数据库文件.db,可以放到assets目录下,然后使用createFromAsset()方法创建Room数据库;或者将.db文件放到SD卡下,使用createFromFile()方法来创建Room数据库。
Room.databaseBuilder(MyApplication.instance(), MyDatabase::class.java, MY_DATABASE_NAME)
.addMigrations(MIGRATION_1_2)
.fallbackToDestructiveMigration()
.createFromAsset("database/students.db")
.build()