持续创作,加快生长!这是我参与「日新方案 10 月更文挑战」的第4天,[点击检查活动详情]
Jetpack从入门到简直入门(四)
(/post/714765… “/post/714765…”)
前言
Jetpack系列:
Jetpack从入门到简直入门(一) – ()
Jetpack从入门到简直入门(二) – ()
Jetpack从入门到简直入门(三) – ()
本文是我在学习guolin大神的《榜首行代码》第三版Jetpack部分的Room的常识总结,文中部分代码参考自《榜首行代码》第三版
在阅读本文前,您需求掌握kotlin语言的根本语法
Room简介
在学习Room之前,每逢提起数据库操作,咱们总是会想到SQLite。SQLite虽然简略易用,但是假如放到大型项目中却会让项目的代码变得紊乱。而Room是基于SQLite上供给的一个笼统层,它不只支撑SQL句子,而且还有许多快捷的功用:
- 具有SQL句子高亮和编译期检查
- 经过简略的注解完结接口的界说,易上手
- 支撑协程、RxJava
- 可以调配AndroidStudio自带的数据库检查窗口运用
Room的增修正查操作
运用Room前要在build.gradle上增加依赖
plugins {
...
id 'kotlin-kapt'
}
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
kapt 'androidx.room:room-compiler:$room_version'
咱们先简略了解一下Room的三个组成部分
-
Entity :用于界说封装实际数据的实体类,每个实体类都在数据库中有一张对应的自动生成的表
-
Dao :Dao 指数据访问目标,对数据库各项操作的封装在此处进行,在实际编程中,逻辑层就不需求和底层数据库打交道,而是接和Dao 层进行交互
-
Database :用于界说数据库中的关键信息,包括数据库的版别号、包括哪些实体类以及提
供Dao 层的访问实例。
接下来咱们创立一个实体类:
@Entity
data class User(var firstName: String, var lastName: String, var age: Int){
@PrimaryKey(autoGenerate = true)
var id:Long = 0
}
在类名上运用@Entity
注解,然后加入id字段,运用@PrimaryKey
注解将它设为主键。autoGenerate
的值设为true,主键的值为自动生成。
接下来开始界说Dao,在这里封装访问数据库的操作(增修正查)。
新建一个UserDao
接口
@Dao
interface UserDao {
@Insert
fun insertUser(user: User): Long
@Update
fun updateUser(newUser: User)
@Query("select * from User")
fun loadAllUsers(): List<User>
@Query("select * from User where age > :age")
fun loadUsersOlderThan(age: Int): List<User>
@Delete
fun deleteUser(user: User)
@Query("delete from User where lastName = :lastName")
fun deleteUserByLastName(lastName: String): Int
}
首先,咱们要在接口前用上@Dao注解,接着咱们进行对增修正查操作进行封装。Room为咱们供给了相应的注解@Insert、@Delete、@Update和@Query。
唯一需求写SQL句子的当地是在查询操作,咱们有必要告知Room需求查询什么当地,其他时分咱们直接运用注解标识即可。这样咱们就大体界说了增加用户、修正用户数据、查询用户、删去用户这几种数据库操作接口。
最终一个环节是界说Database,新建一个AppDatabase.kt 文件
@Database(version = 1, entities = [User::class]) //声明数据库的版别号,包括哪些实体类,多个实体类之间可用逗号隔开
abstract class AppDatabase : RoomDatabase(){
abstract fun userDao(): UserDao //供给笼统办法,用于获取已编写的Dao实例;只声明办法,具体完成在Room底层自动完结
companion object { //Kotlin的类中不答应你声明静态成员或办法,要增加Companion目标来包装这些静态引证
private var instance: AppDatabase? = null
@Synchronized
fun getDatabase(context: Context) : AppDatabase {
instance?.let {
return it //instance变量不为空直接返回
}
//否则就调用Room.databaseBuilder()办法来构建一个AppDatabase的实例
return Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java, "app_database")
.build().apply {
instance = this
}
}
}
}
接下来咱们修正页面,加上增修正查的4个按钮
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<Button
android:id="@+id/getUserBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Get User"/>
<Button
android:id="@+id/addDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Add Data"/>
<Button
android:id="@+id/updateDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Update Data"/>
<Button
android:id="@+id/deleteDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Delete Data"/>
<Button
android:id="@+id/queryDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Query Data"/>
</LinearLayout>
在MainActivity中完成代码的逻辑
class MainActivity : AppCompatActivity() {
val userDao = AppDatabase.getDatabase(this).userDao()
val user1 = User("Tom", "Brady", 40)
val user2 = User("Tom", "Hanks", 63)
val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
...
binding.addDataBtn.setOnClickListener {
thread {
user1.id = userDao.insertUser(user1)
user2.id = userDao.insertUser(user2) //更新ID
}
}
binding.updateDataBtn.setOnClickListener {
thread {
user1.age = 42
userDao.updateUser(user1)
}
}
binding.deleteDataBtn.setOnClickListener {
thread {
userDao.deleteUserByLastName("Hanks")
}
}
binding.queryDataBtn.setOnClickListener {
thread {
for (user in userDao.loadAllUsers()) {
Log.d("MainActivity", user.toString())
}
}
}
...
}
值得注意的是,数据库操作是耗时操作,所以Room默许不能在主线程中操作,但是Room也供给了办法答应咱们在主线程进行操作:
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java,"app_database")
.allowMainThreadQueries()
.build()
不过此办法只主张在测试环境下运用。
Room的数据库更新
进行数据库升降级时,咱们可以运用addMigration
进行操作,在AppDatabase的getDatabase中编写instance:
instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"user.db"
).addMigrations(object :Migration(1,2){ //从版别1到版别2,降级则倒转
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE user ADD age INTEGER Default 0 not null ")
}
})allowMainThreadQueries().build()
Room还供给了两个办法:
-
fallbackToDestructiveMigration
:升级时可运用该办法,当未匹配到版别的时分就会直接删去表然后从头创立。 -
fallbackToDestructiveMigrationOnDowngrade
:降级时可增加此办法,当未匹配到版别的时分就会直接删去表然后从头创立。
总结
学会