前言
Room 在 SQLite 的基础上提供了一个抽象层,除了拥有 SQLite 的全部功能,使用起来还非常方便。
具体来说,Room 有以下优势:
-
- 编译期对 SQL 查询进行校验;
-
- 提供方便的注解,减少重复且容易出错的样板代码;
-
- 简化的数据库迁移路径;
因为这些优势,强烈建议使用 Room 而不是直接使用 SQLite APIs.
主要组成部分
Room 包含 3 个主要组成部分:
- 数据库类(database class),持有数据库,作为访问应用存储的数据的底层连接的主要入口。
- Data entities,代表你的应用的数据库里面的表。
- Data access objects (DAOs),提供函数用于 查询、更新、插入和删除 数据库。
这 3 个组件的概念也出现在其他 ORM(Object Relational Mapping——对象关系映射)框架中,通过 Database 获取 DAO ,通过 DAO 查询并获取 Entities,最后通过 Entities 对数据库表中的数据进行读写。
简单使用
1. 引入依赖
使用 Room 需引入如下依赖:
dependencies {
val room_version = "2.5.2"
// 基础依赖
implementation("androidx.room:room-runtime:$room_version")
// 如果该项目使用了 Kotlin,请使用 Kotlin 符号处理器 (Kotlin Symbol Processing)
ksp("androidx.room:room-compiler:$room_version")
// 如果该项目只用了 Java,使用 Java 的注解处理器(annotationProcessor)
annotationProcessor("androidx.room:room-compiler:$room_version")
// 可选项 - 为 Room 提供 Kotlin 扩展功能与协程支持
implementation("androidx.room:room-ktx:$room_version")
}
使用 KSP 还需要给你的项目添加 KSP Plugin,先去项目中的 build.gradle.kts 中声明 KSP Plugin:
plugins {
id("com.google.devtools.ksp") version "2.0.0-1.0.21" apply false
}
注意 KSP 需要与 Kotlin 的版本对齐,如果你使用的是 Kotlin 2.0.0,那么 KSP 的版本必须是 2.0.0-x.y.z。
然后在模块的 build.gradle.kts 中启用 KSP:
plugins {
id("com.google.devtools.ksp")
}
2.定义实体类 Entity
该 Entity 对应数据库中的表
@Entity
data class User(
// 主键,自增
@PrimaryKey(autoGenerate = true)
var userId: Int = 0,
@ColumnInfo(name = "name")
var name: String = "",
// name 表示表中对应的名字,可以定义成一个不同的名字
@ColumnInfo(name = "pwd")
var password: String = "",
@ColumnInfo(name="address")
var address: String = ""
)
另外定义一个类 UserTuple,用于只获取表中的 name 和 pwd 字段:
@Entity
data class UserTuple(
@ColumnInfo(name = "name")
var name: String,
@ColumnInfo(name = "pwd")
var password: String,
)
3.定义 DAO 用于对数据库进行增删改查
@Dao
interface UserDao {
@Insert
fun insert(vararg users: User?)
@Delete
fun delete(user: User?)
@Update
fun update(user: User?)
@Query("select * from User")
fun queryAll(): List<User?>?
@Query("select * from User where name like :name")
fun queryByName(name: String?): User?
@Query("select * from User where userId in (:userIds)")
fun queryInUserId(userIds: IntArray?): List<User?>?
// 只查询 name, pwd 字段
@Query("select name, pwd from User")
fun getRecord(): List<UserTuple?>?
}
4.定义数据库类用于获取 Dao
// entities 指定了这个数据库包含的实体类(即表),可以包含多个实体类,用逗号分隔。
// exportSchema 这个参数表示是否导出数据库的架构。
// 通常在生产环境中设置为 false,表示不导出;
// 而在开发和调试时可以设置为 true,以便生成数据库的架构文件。
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase: RoomDatabase() {
abstract fun getUserDao(): UserDao
}
5.对数据库进行操作
class MainActivity : AppCompatActivity() {
private val TAG = this::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java,"user")
//.allowMainThreadQueries() 可以允许在主线程查询
.build()
val dao = db.getUserDao()
dao.insert(User(name = "张三", password = "111", address = "北京市"))
dao.insert(User(name = "李四", password = "123", address = "天津市"))
dao.insert(User(name = "王五", password = "456", address = "南京市"))
val userList = dao.queryAll()
Log.i(TAG, userList.toString())
val user = dao.queryByName("王五")
Log.i(TAG, user.toString())
val listId = dao.queryInUserId(intArrayOf(2, 3))
Log.i(TAG, listId.toString())
val records = dao.getRecord()
Log.i(TAG, records.toString())
}
}
}
运行后打印如下:
[User(userId=1, name=张三, password=111, address=北京市), User(userId=2, name=李四, password=123, address=天津市), User(userId=3, name=王五, password=456, address=南京市)]
User(userId=3, name=王五, password=456, address=南京市)
[User(userId=2, name=李四, password=123, address=天津市), User(userId=3, name=王五, password=456, address=南京市)]
[UserTuple(name=张三, password=111), UserTuple(name=李四, password=123), UserTuple(name=王五, password=456)]