目录
- ROOM是什么
- 为什么出现呢?
- 如何使用呢?
- 项目中如何使用呢?
- 数据库迁移
一、ROOM是什么
Jetpack中的一门技术,Room 是 Google 推出的一个在 SQLite 上提供抽象层的持久存储库。使用ROOM我们可以快速的生成数据库,表,可以快速的去访问。
涉及到的知识点:Entity,Dao,Database,Migration,Repository。
**Entity:**这是一个Model类,对应于数据库中的一张表。Entity类是Sqlite表结构在Java类的映射。
**Dao:**数据访问对象,我们可以通过它来增删改查,每一张表对应一个Dao。
Database:
Migration:
Repository:
二、为什么出现呢?
流畅地访问 SQLite 数据库。
三、如何使用呢?
1.增加插件:由于 Room框架中使用了注解处理器,因此需要使用 kapt 依赖,需要在 build.gradle 文件中引入 kapt 插件。
id 'kotlin-kapt'
2.引入依赖
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
id("kotlin-kapt")
}
dependencies {
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
}
3.创建一个关于用户的Entity,即创建一张用户表。
(1)Entity
@Entity(tableName = "user")
data class UserEntity(
@PrimaryKey(autoGenerate = true)
var id: Int?,
@ColumnInfo
var account:String,
@ColumnInfo
var nickname:String
)
如果不想传递id?
(2)Dao
@Dao
interface UserDao {
@Query("select * from user")
fun getAll():List<UserEntity>
@Insert
fun insertUser(userEntity: UserEntity)
@Query("select * from user where account=:account")
fun getInfoByAct(account:String):List<UserEntity>
}
(3)DataBase:为什么要搞抽象的呢?【因为我们只是定义信息,实例化是由ROOM来实现的。】
这是一个数据库,需要协助数据库的版本号,有哪些数据表.
注意,数据库对象不推荐一直创建的,消耗资源,所以我们需要做成单例的。
@Database(
entities = [UserEntity::class],
version = 1
)
abstract class MyDataBase:RoomDatabase() {
companion object{
private var instance:MyDataBase?=null //可空类型 在Kotlin中,默认情况下,所有基本数据类型(如 Int, Double 等)都是非空的
fun getInstance(context:Context):MyDataBase{
if (instance==null) {
Room.inMemoryDatabaseBuilder(context,MyDataBase::class.java)
.build()
.apply {
instance = this
}
}
//当你有一个可空类型的变量,但在某个上下文中你知道它绝对不会是 null,你可以使用 !! 来告诉Kotlin编译器这个变量是非空的,并直接解引用它。但是,如果变量实际上是 null,使用 !! 会导致运行时异常
return instance!!//非空断言 如果条件判断失败(即 instance 没有被成功初始化),这里就会抛出 NullPointerException。
}
}
abstract fun getUserDao():UserDao
}
(4) 使用:数据库的插入和查询,我开启了协程,因为都是耗时操作。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val userDao = MyDataBase.getInstance(this).getUserDao()
var button : Button = findViewById(R.id.button)
var text: TextView = findViewById(R.id.textView)
button.setOnClickListener{
lifecycleScope.launch (Dispatchers.IO) {
val all = userDao.getAll()
lifecycleScope.launch(Dispatchers.Main) {
text.text= all.toString()
}
}
}
var button2 : Button = findViewById(R.id.button2)
button2.setOnClickListener{
lifecycleScope.launch (Dispatchers.IO){
userDao.insertUser(UserEntity(0,"account1","昵称"))
}
}
}
}
如果你不想传递id,可以这样,指定参数去赋值。
lifecycleScope.launch (Dispatchers.IO){
userDao.insertUser(UserEntity(
account = "account1", nickname = "昵称"
))
}
3.1 生成文件
上述的数据库,其实都是生成在内存当中,如果程序重启数据就会丢失。如果想存储到文件里面,我们需要将inMemoryDatabaseBuilder方法换成databaseBuilder方法,这个方法,需要提供一个数据库名
Room.databaseBuilder(context,MyDataBase::class.java,"UserDb")
.build()
.apply {
instance = this
}
3.2 导出数据库的架构
exportSchema: 这个参数是一个布尔值,用于指示是否应该导出数据库的架构。如果设置为 true,Room 会在编译时生成一个包含数据库架构的 JSON 文件。这对于调试和文档记录可能是有用的,但在生产环境中通常不需要。在这个例子中,它被设置为 false,意味着不会导出架构。
@Database(
entities = [UserEntity::class],
version = 1,
exportSchema = false
)
abstract class MyDataBase:RoomDatabase() {
3.3 多个activity使用数据库
- 如下如果是多个activity,会使用到数据库,那么就需要增加如下这句代码。因为他是查询是将数据加载到内存的,所以如果这个时候有其他使用到数据库,那么数据就会出现不一致的问题,所以需要增加这句代码。
Room.databaseBuilder(context,MyDataBase::class.java,"UserDb")
.enableMultiInstanceInvalidation()
.build()
.apply {
instance = this
}
四、项目中如何使用呢
其实就是如我们上面这样写,没有高级的工具
(1)多个表:entities可以这样写
entities = [ProjectClassify::class, Article::class, HotKey::class, BannerBean::class,Almanac::class],
然后提供多个Dao
abstract fun projectClassifyDao(): ProjectClassifyDao
abstract fun browseHistoryDao(): BrowseHistoryDao
abstract fun hotKeyDao(): HotKeyDao
abstract fun bannerBeanDao(): BannerBeanDao
abstract fun almanacDao(): AlmanacDao
大家,有兴趣,可以多上github找一些项目学习,看看别人是如何写的。
五、数据库迁移
如果我们想在这个表上增加一个字段,应该如何操作呢?
比如我们想增加一个foot字段
@Entity(tableName = "user")
data class UserEntity(
@PrimaryKey(autoGenerate = true)
val id: Int =0, // 改为非空
@ColumnInfo
val account: String,
@ColumnInfo
val nickname: String,
@ColumnInfo
val foot :Boolean
)
新增字段,要对数据库的版本号进行提升。比如我们从1,提升到2
@Database(
entities = [UserEntity::class],
version = 2,
exportSchema = false
)
只是修改这里还不行,我们还需要设置升级的策略。
(1)第一种:删除表,重新创建,暴力清除策略
abstract class MyDataBase:RoomDatabase() {
companion object{
private var instance:MyDataBase?=null //可空类型 在Kotlin中,默认情况下,所有基本数据类型(如 Int, Double 等)都是非空的
fun getInstance(context:Context):MyDataBase{
if (instance==null) {
Room.databaseBuilder(context,MyDataBase::class.java,"UserDb")
.enableMultiInstanceInvalidation()
.fallbackToDestructiveMigration()//暴力式清除
.build()
.apply {
instance = this
}
}
//当你有一个可空类型的变量,但在某个上下文中你知道它绝对不会是 null,你可以使用 !! 来告诉Kotlin编译器这个变量是非空的,并直接解引用它。但是,如果变量实际上是 null,使用 !! 会导致运行时异常
return instance!!//非空断言 如果条件判断失败(即 instance 没有被成功初始化),这里就会抛出 NullPointerException。
}
}
abstract fun getUserDao():UserDao
}
(2)自定义:你可以指定一个方式,比如你要新增,则写一个sql语句
val MIGRATION_1_2 = object :Migration(1,2){
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE user ADD COLUMN foot INTEGER NOT NULL DEFAULT 1 "
)
}
}
abstract class MyDataBase:RoomDatabase() {
companion object{
private var instance:MyDataBase?=null //可空类型 在Kotlin中,默认情况下,所有基本数据类型(如 Int, Double 等)都是非空的
fun getInstance(context:Context):MyDataBase{
if (instance==null) {
Room.databaseBuilder(context,MyDataBase::class.java,"UserDb")
.enableMultiInstanceInvalidation()
// .fallbackToDestructiveMigration()//暴力式清除
.addMigrations(MIGRATION_1_2)
.build()
.apply {
instance = this
}
}
//当你有一个可空类型的变量,但在某个上下文中你知道它绝对不会是 null,你可以使用 !! 来告诉Kotlin编译器这个变量是非空的,并直接解引用它。但是,如果变量实际上是 null,使用 !! 会导致运行时异常
return instance!!//非空断言 如果条件判断失败(即 instance 没有被成功初始化),这里就会抛出 NullPointerException。
}
}
abstract fun getUserDao():UserDao
}
好了,以上就是ROOM的基础内容了。