一、前言
视图是一个或者多个基本表(视图)构建的一个虚拟表,使用视图有许多的好处,其中主要的两个好处是封装查询和灵活的安全性控制。Room 从2.1.0 及更高版本开始支持 SQLite 数据库视图功能,你可以将查询封装到一个类中, Room 将这些支持查询的类称之为视图,视图在 DAO 中使用跟普通的数据实体对象一样。更多关于视图的相关内容大家可以自行学习,在本篇幅,我们将详细介绍在 Room 中创建视图并使用。
注意事项:跟数据实体类一样,你可以在视图中执行
SELECT
查询语句,但是你无法在视图中执行INSERT
、UPDATE
、DELETE
语句。换句话说,视图只有读权限,没有写权限,这就是安全性控制的一个特点。
二、 在Room中使用视图
2.1 创建视图类
Room 创建视图非常简单,定义一个数据类(跟数据实体类一样,不过对应的是视图,不是实体表),使用 @DatabaseView
注解进行标注,@DatabaseView
有两个参数,一个是 viewName
(自定义视图名称,可选),另一个是 values
(视图数据对应的 SQL 查询语句)。如下示例所示:
// 数据实体类(实体表),视图类对应数据实体类(实体表)
@Entity(tableName = "schools")
data class School(@PrimaryKey val sid: Int, val name: String, val addr: String)
@Entity(tableName = "students")
data class Student(@PrimaryKey val sid: Int, val name: String, val age: Int, val schoolId: Int, val addr: String?)
// 创建视图类,@DatabaseView 注解可以用 viewName 参数指定视图名称,如果不指定视图名称,@DatabaseView 注解默认参数是 value
//@DatabaseView(viewName = "student_school", value = "SELECT students.sid, students.name, students.age, " +
// "schools.sid AS schoolId, schools.name AS schoolName " +
// "FROM students INNER JOIN schools ON students.schoolId = schools.sid")
@DatabaseView("SELECT students.sid, students.name, students.age, " +
"schools.sid AS schoolId, schools.name AS schoolName " +
"FROM students INNER JOIN schools ON students.schoolId = schools.sid")
data class StudentWithSchool(
val sid: Int,
val name: String,
val age: Int,
val schoolId: Int,
val schoolName: String)
2.2 在数据库类中声明视图
创建了视图类之后,需要在数据库定义时声明视图类。在 @Database
注解中的 views
参数进行声明,如下示例所示:
@Database(
entities = [User::class, School::class, Student::class, Profile::class],
views = [StudentWithSchool::class],
version = 1)
abstract class AppDatabase: RoomDatabase() {
abstract fun studentDao(): StudentDao
}
2.3 查询视图获取数据
在 DAO 类中添加查询视图方法,即可从视图中查询数据。
@Dao
interface StudentDao {
@Insert
fun insertAll(vararg student: Student)
@Insert
fun insertSchool(vararg schools: School)
// 查询视图表
@Query("SELECT * from StudentWithSchool")
suspend fun getStudentWithSchool() : List<StudentWithSchool>
}
注意事项:视图表只能执行
SELECT
查询操作,不能执行INSERT
、UPDATE
、DELETE
操作。
2.4 数据库迁移时视图的处理
视图是一个虚拟表,所以在数据库升级迁移时,视图发生变更(新增、删除、修改等)时,也需要进行处理。数据库迁移相关可请参考文章:Android Room 数据库详解。下面通过示例介绍下数据库迁移时视图的处理。
- 数据库迁移前
// 定义数据库
@Database(entities = [User::class, School::class, Student::class, Profile::class], version = 1)
abstract class AppDatabase: RoomDatabase() {
abstract fun studentDao(): StudentDao
}
// 构建数据库类
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app.db")
.build()
- 数据库迁移后(新增视图)
// 定义数据库
@Database(entities = [User::class, School::class, Student::class, Profile::class], views = [StudentWithSchool::class], version = 2)
abstract class AppDatabase: RoomDatabase() {
abstract fun studentDao(): StudentDao
}
// 定义数据库迁移配置
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// 这里新建一个视图(视图名称要用两个半角的间隔号括起来)
database.execSQL("CREATE VIEW `StudentWithSchool` AS SELECT students.sid, students.name, students.age, schools.sid AS schoolId, schools.name AS schoolName FROM students INNER JOIN schools ON students.schoolId = schools.sid")
}
}
// 构建数据库类
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app.db")
.addMigrations(migration_1_2) // 添加数据迁移配置
.build()
示例讲解:上面的示例中,数据库迁移过程中新增一个视图,在迁移配置中必须手动创建一个视图。跟数据表一样,在
@Database
中声明的视图类,只会在第一次安装创建数据库时才会创建视图,后续添加的必须进行数据库迁移操作。创建视图使用CREATE VIEW
语法(更多 SQL 语法大家自行学习),不过笔者发现一个很有趣的现象,SQL 语句创建视图时,视图名称直接用字符窜表示即可,但是在 Room 的数据库迁移配置中,必须使用两个半角的间隔号(就是键盘上方 1 数字键左边的那个按键,并不是单引号,这个需要特别注意)。