一、Room 框架使用核心要点
1、build.gradle 构建脚本配置说明
对于 Room 框架 来说 , 使用 Java 语言 开发和使用 Kotlin 语言 开发 , 需要在 build.gradle 构建脚本 中进行不同的配置 , 主要有以下两个配置不同 :
- 应用的插件不同 ;
- 导入依赖库方式不同 ;
应用插件
应用的插件区别 :
- 如果使用 Java 语言开发 , 只需要导入 android 插件 ;
plugins {
id 'com.android.application'
}
- 如果使用 Kotlin 语言开发 , 需要在 android 插件 的基础上 额外导入 kotlin 和 kotlin-kapt 插件 ;
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
导入依赖
导入依赖库区别 : 如果导入错误 , 就会出现
- 如果使用 Java 语言开发 , 需要使用 annotationProcessor 导入注解处理器 , 用于在编译期间生成代码 ;
// 导入 Room 依赖库
implementation 'androidx.room:room-runtime:2.2.5'
// 导入注解处理器 ( Java )
annotationProcessor 'androidx.room:room-compiler:2.2.5'
- 如果使用 Kotlin 语言开发 , 需要使用 kapt 导入注解处理器 , 用于在编译期间生成代码 ;
// 导入 Room 依赖库
implementation 'androidx.room:room-runtime:2.2.5'
// 导入注解处理器 ( Kotlin )
kapt 'androidx.room:room-compiler:2.2.5'
2、定义 Entity 实体类
使用 Room 框架 的应用中 , Entity 实体类 对应着 数据库表 , 将 Entity 实体类 定义完成后 , 就意味着 数据库表的结构 已经定义完成 ;
@Entity 注解
Entity 实体类 需要使用 @Entity 注解进行修饰 , 该注解可以 传入 tableName 参数 , 该 tableName 参数的作用是定义数据库表的名称 ;
代码语言:javascript复制@Entity(tableName = "student")
class Student {
}
@PrimaryKey 注解
定义主键 , 使用 @PrimaryKey 注解修饰主键 , 设置 autoGenerate = true 参数 可以令 主键自增 ;
数据库表 列信息 使用 @ColumnInfo 注解定义 , 该注解中的参数 :
- name = “id” 定义了 数据表的列名称 ;
- typeAffinity = ColumnInfo.INTEGER 定义该 列字段的类型是 int 整型 ;
/**
* @PrimaryKey 设置主键 autoGenerate 为自增
* @ColumnInfo name 设置列名称 / typeAffinity 设置列类型
*/
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
var id: Int = 0
@ColumnInfo 注解
定义普通字段 , 只需要使用 @ColumnInfo
注解修饰字段即可 , 通过 name = "name"
设置数据库表名称 , typeAffinity = ColumnInfo.TEXT
设置该列的类型为 String 类型 ;
/**
* 姓名字段
* 数据库表中的列名为 name
* 数据库表中的类型为 TEXT 文本类型
*/
@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
lateinit var name: String
@Ignore 注解
修饰字段
如果 有些字段 不需要设置为数据库表列 , 仅用于业务逻辑中使用 , 不需要插入数据库中 , 使用 @Ignore
修饰该字段即可 ;
/**
* 有些属性用于做业务逻辑
* 不需要插入到数据库中
* 使用 @Ignore 注解修饰该属性字段
*/
@Ignore
lateinit var studentInfo: String
修饰函数
使用 @Ignore 注解标注构造函数后 , Room 框架就不会使用该构造方法了 ;
下面的 3 个构造函数中 ,
- Room 框架 使用
constructor(id: Int, name: String, age: Int)
构造函数 ; - 开发者 使用
constructor(name: String, age: Int)
和constructor(id: Int)
构造函数 ;
/**
* 默认的构造方法给 Room 框架使用
*/
constructor(id: Int, name: String, age: Int) {
this.id = id
this.name = name
this.age = age
}
/**
* 使用 @Ignore 注解标注构造函数后
* Room 就不会使用该构造方法了
* 这个构造方法是给开发者使用的
*/
@Ignore
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
/**
* 使用 @Ignore 标签标注后
* Room 就不会使用该构造方法了
* 这个构造方法是给开发者使用的
*/
@Ignore
constructor(id: Int) {
this.id = id
}
3、定义 Dao 数据库访问对象接口
@Dao 注解
定义的 Dao 数据库访问对象接口 是一个 interface 接口 , 使用 @Dao 注解修饰该接口 ;
代码语言:javascript复制/**
* 数据库访问对象接口 / 使用 @Dao 注解修饰
* 提供数据库的增删改查方法
*/
@Dao
interface StudentDao {
@Insert 注解
向数据库中插入数据 , 使用 @Insert 注解修饰对应的抽象方法 ;
代码语言:javascript复制 /**
* 向数据库表中插入元素
*/
@Insert
fun insert(student: Student)
@Delete 注解
从数据库中删除数据 , 使用 @Delete 注解修饰对应的抽象方法 ;
代码语言:javascript复制 /**
* 从数据库表中删除元素
*/
@Delete
fun delete(student: Student)
@Update 注解
更新数据库中的数据 , 使用 @Update 注解修饰对应的抽象方法 ;
代码语言:javascript复制 /**
* 修改数据库表元素
*/
@Update
fun update(student: Student)
@Query 注解
查询数据库中的数据 , 使用 @Query 注解修饰对应的抽象方法 ;
注解中可以设置字符串参数 , 该字符串参数就是查询的 SQL 语句 , 使用 冒号 :
可访问传入的参数 ;
/**
* 查询数据库表
*/
@Query("select * from student")
fun query(): List<Student>
/**
* 根据传入的 id 查询数据库表
* 在注解中使用 :id 调用参数中的 id: Int
*/
@Query("select * from student where id = :id")
fun query(id: Int): List<Student>
4、定义 RoomDatabase 数据库实例类
定义的 RoomDatabase 数据库实例类 是一个 抽象类 , 需要继承 RoomDatabase 抽象类 , 同时要使用 @Database
注解修饰 ,
@Database(entities = [Student::class], version = 1, exportSchema = false)
abstract class StudentDatabase: RoomDatabase() {
在该抽象类中定义抽象方法 , 获取 数据库访问 对象 ,
代码语言:javascript复制 /**
* 获取 数据库访问 对象
* 这是必须要实现的函数
*/
abstract fun studentDao(): StudentDao
将该类设置成单例类 , 在单例类对象初始化时 , 创建数据库 ;
代码语言:javascript复制 companion object {
lateinit var instance: StudentDatabase
fun inst(context: Context): StudentDatabase {
if (!::instance.isInitialized) {
synchronized(StudentDatabase::class) {
// 创建数据库
instance = Room.databaseBuilder(
context.applicationContext,
StudentDatabase::class.java,
"student_database.db")
.allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
// 如果要在主线程操作数据库需要调用该函数
.build()
}
}
return instance;
}
}
初始化数据库代码 :
- 首先 , 调用 Room.databaseBuilder 函数 , 创建 RoomDatabase.Builder 对象 , 传入 Context 上下文 , StudentDatabase 类对象 , 数据库名称 等参数 ;
- 然后 , 调用 RoomDatabase.Builder 对象的 build 函数 , 创建数据库 ;
- 注意 : Room 原则上不允许在主线程操作数据库 , 如果要在主线程操作数据库 需要调用 RoomDatabase.Builder # allowMainThreadQueries 函数 , 允许主线程操作 ;
// 创建数据库
instance = Room.databaseBuilder(
context.applicationContext,
StudentDatabase::class.java,
"student_database.db")
.allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
// 如果要在主线程操作数据库需要调用该函数
.build()
5、调用 Room 框架访问数据库
首先 , 获取 RoomDatabase 数据库实例类 , 调用其单例的获取函数即可 , 调用该函数 , 即可创建对应的数据库 ;
代码语言:javascript复制 // 获取 StudentDatabase
var studentDatabase: StudentDatabase = StudentDatabase.inst(this)
然后 , 获取 数据库访问对象 Dao , 通过 RoomDatabase 数据库实例类 的抽象方法获取 ;
代码语言:javascript复制 // 获取数据库访问对象
var studentDao: StudentDao = studentDatabase.studentDao()
最后 , 使用 Dao 数据库访问对象 , 进行数据库访问操作 , 推荐在线程中访问数据库 ;
代码语言:javascript复制 thread(start = true) {
// 插入数据
var s1 = Student("Tom", 18)
var s2 = Student("Jerry", 16)
studentDao.insert(s1)
studentDao.insert(s2)
// 查询数据
var students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 插入后首次查询 ) : " students)
// 更新数据 , 将学生年龄都设置为 20
for (i in 0.. students.size - 1) {
students[i].age = 20
studentDao.update(students[i])
}
students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 修改后查询结果 ) : " students)
// 删除数据
var s_delete = Student(1) // 删除的元素只需要传入 id 即可
studentDao.delete(s_delete)
students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 删除后查询结果 ) : " students)
}
二、完整代码示例
1、build.gradle 构建脚本
在 build.gradle 构建脚本中 , 主要是导入 Kotlin 插件 , 和 Kotlin 注解插件 ;
如果使用 Java 语言开发 , 则不需要导入这两个插件 ;
代码语言:javascript复制 id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
导入 Room 依赖库 和 编译时生成代码的 注解处理器 ;
代码语言:javascript复制 // 导入 Room 依赖库
implementation 'androidx.room:room-runtime:2.2.5'
// 导入注解处理器 ( Kotlin )
kapt 'androidx.room:room-compiler:2.2.5'
// 导入注解处理器 ( Java )
//annotationProcessor 'androidx.room:room-compiler:2.2.5'
完整代码如下 :
代码语言:javascript复制plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
namespace 'kim.hsl.roomdemo'
compileSdk 32
defaultConfig {
applicationId "kim.hsl.roomdemo"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
viewBinding {
// 启用 ViewBinding
enabled = true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// 导入 Room 依赖库
implementation 'androidx.room:room-runtime:2.2.5'
// 导入注解处理器 ( Kotlin )
kapt 'androidx.room:room-compiler:2.2.5'
// 导入注解处理器 ( Java )
//annotationProcessor 'androidx.room:room-compiler:2.2.5'
}
2、Entity 实体类代码
代码语言:javascript复制package kim.hsl.roomdemo
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
/**
* 定义数据库表 Entity 实体 / 同时定义数据库表 和 对鹰的实体类
* 设置该数据类对应数据库中的一张数据表, 表名为 student
* 该数据库表中的数据对应一个 Student 类实例对象
*/
@Entity(tableName = "student")
class Student {
/**
* @PrimaryKey 设置主键 autoGenerate 为自增
* @ColumnInfo name 设置列名称 / typeAffinity 设置列类型
*/
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
var id: Int = 0
/**
* 姓名字段
* 数据库表中的列名为 name
* 数据库表中的类型为 TEXT 文本类型
*/
@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
lateinit var name: String
/**
* 年龄字段
* 数据库表中的列名为 age
* 数据库表中的类型为 INTEGER 文本类型
*/
@ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
var age: Int = 0
/**
* 有些属性用于做业务逻辑
* 不需要插入到数据库中
* 使用 @Ignore 注解修饰该属性字段
*/
@Ignore
lateinit var studentInfo: String
/**
* 默认的构造方法给 Room 框架使用
*/
constructor(id: Int, name: String, age: Int) {
this.id = id
this.name = name
this.age = age
}
/**
* 使用 @Ignore 注解标注构造函数后
* Room 就不会使用该构造方法了
* 这个构造方法是给开发者使用的
*/
@Ignore
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
/**
* 使用 @Ignore 标签标注后
* Room 就不会使用该构造方法了
* 这个构造方法是给开发者使用的
*/
@Ignore
constructor(id: Int) {
this.id = id
}
override fun toString(): String {
return "Student(id=$id, name='$name', age=$age)"
}
}
3、Dao 数据库访问对象接口代码
使用 @Dao 注解修饰接口类 ;
分别使用 @Insert , @Delete , @Update , @Query 注解 修饰对应的 增加 , 删除 , 修改 , 查询 等 函数 ;
完整代码 :
代码语言:javascript复制package kim.hsl.roomdemo
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
/**
* 数据库访问对象接口 / 使用 @Dao 注解修饰
* 提供数据库的增删改查方法
*/
@Dao
interface StudentDao {
/**
* 向数据库表中插入元素
*/
@Insert
fun insert(student: Student)
/**
* 从数据库表中删除元素
*/
@Delete
fun delete(student: Student)
/**
* 修改数据库表元素
*/
@Update
fun update(student: Student)
/**
* 查询数据库表
*/
@Query("select * from student")
fun query(): List<Student>
/**
* 根据传入的 id 查询数据库表
* 在注解中使用 :id 调用参数中的 id: Int
*/
@Query("select * from student where id = :id")
fun query(id: Int): List<Student>
}
4、RoomDatabase 数据库实例类代码
RoomDatabase 数据库实例类 需要继承 RoomDatabase 抽象类 , 使用 @Database 注解修饰 该抽象类 ;
其中需要 定义 获取 数据库访问 对象 的抽象函数 ;
将该类定义成 单例类 , 在单例对象初始化时 , 创建数据库 ;
完整代码 :
代码语言:javascript复制package kim.hsl.roomdemo
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [Student::class], version = 1, exportSchema = false)
abstract class StudentDatabase: RoomDatabase() {
/**
* 获取 数据库访问 对象
* 这是必须要实现的函数
*/
abstract fun studentDao(): StudentDao
companion object {
lateinit var instance: StudentDatabase
fun inst(context: Context): StudentDatabase {
if (!::instance.isInitialized) {
synchronized(StudentDatabase::class) {
// 创建数据库
instance = Room.databaseBuilder(
context.applicationContext,
StudentDatabase::class.java,
"student_database.db")
.allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
// 如果要在主线程操作数据库需要调用该函数
.build()
}
}
return instance;
}
}
}
5、在 Activity 组件中通过调用 Room 框架访问数据库
首先 , 获取 数据库实例类 StudentDatabase ;
然后 , 获取 数据库访问对象接口 StudentDao ;
最后 , 调用 数据库访问对象接口 StudentDao 的一系列方法访问数据库 ;
完整代码 :
代码语言:javascript复制package kim.hsl.roomdemo
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kim.hsl.roomdemo.databinding.ActivityMainBinding
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(getLayoutInflater())
setContentView(binding.root)
// 获取 StudentDatabase
var studentDatabase: StudentDatabase = StudentDatabase.inst(this)
// 获取数据库访问对象
var studentDao: StudentDao = studentDatabase.studentDao()
thread(start = true) {
// 插入数据
var s1 = Student("Tom", 18)
var s2 = Student("Jerry", 16)
studentDao.insert(s1)
studentDao.insert(s2)
// 查询数据
var students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 插入后首次查询 ) : " students)
// 更新数据 , 将学生年龄都设置为 20
for (i in 0.. students.size - 1) {
students[i].age = 20
studentDao.update(students[i])
}
students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 修改后查询结果 ) : " students)
// 删除数据
var s_delete = Student(1) // 删除的元素只需要传入 id 即可
studentDao.delete(s_delete)
students = studentDao.query()
Log.i("MainActivity", "数据库查询结果 ( 删除后查询结果 ) : " students)
}
}
}