文章内容包括:
- Room的介绍和基本使用
- 数据库的创建
- 表的创建
- DAO (数据库访问对象)的创建
- 数据库升级(添加列、删除列)
- 结合ViewModel使用
项目依赖
plugins {
...
id 'kotlin-kapt'
}
dependencies {
...
// room
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
Room介绍
Room 谷歌官网介绍
Room 持久性库在 SQLite 上提供了一个抽象层,
相对于SQLiteOpenHelper等传统方法,使用Room操作SQLite有以下优势:
- 编译期的SQL语法检查
- 开发高效,避免大量模板代码
- API设计友好,容易理解
- 可以LiveData关联,具备LiveData Lifecycle 的所有魅力
Room的使用,主要涉及以下3个组件
- Database: 访问底层数据库的入口
- Entity: 代表数据库中的表(table),一般用注解
- Data Access Object (DAO): 数据库访问对象
一、Entity使用(相当于建一张表)
/**
* 实体类,相当表创建一张表
* 1. 版本2时,添加字段phone
* 2. 版本3时,删除字段pwd
*/
@Entity(tableName = "student")
data class Student(
@ColumnInfo(name = "name") var name: String,
/* @ColumnInfo(name = "pwd") var pwd: String,*/
@ColumnInfo(name = "phone") var phone: String = ""
) {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "sid")
var sid: Int = 0
}
二、DAO使用(通过DAO访问数据库操作表)
/**
* 数据库访问对象
*/
@Dao
interface StudentDao {
// 增
@Insert
fun insert(vararg students: Student)
// 删
@Delete
fun delete(vararg students: Student)
// 清空表数据
@Query("delete from student")
fun deleteAll()
// 根据id列表查询
@Query("delete from student where sid in(:sids) ")
fun deleteStudentByIds(vararg sids: Int)
// 改
@Update
fun update(vararg students: Student)
// 改id改名称
@Query("update student set name=:name where sid=:sid ")
fun update(name: String, sid: Int)
// 根据sid 查询一个
@Query("select * from student where sid=:sid")
fun query(sid: Int): Student
// 根据sids查询
@Query("select * from student where sid in(:sids)")
fun queryAll(vararg sids: Int): List<Student>
// 查询所有,与LiveData结合使用
@Query("select * from student")
fun queryAll(): LiveData<List<Student>>
}
三、Database使用(数据库初始化)
/**
* 本地数据库-->可以存放多张表
*/
@Database(
entities = [Student::class],
version = 3,
exportSchema = false
)
abstract class AppDataBase : RoomDatabase() {
// Student数据访问对象
abstract fun getStudentDao(): StudentDao
companion object {
@Volatile
private var dataBase: AppDataBase? = null
private val countDownLatch = CountDownLatch(1)
/**
* 1. 在Application先调用init方法
* 2. 获取数据库,会阻塞
*/
fun getAppDataBase(): AppDataBase {
if (dataBase == null) {
countDownLatch.await()
}
return dataBase!!
}
// 初始化
fun init(context: Context) {
// thread别写了Thread ,如果写就这样写Thread{}.start()
thread {
try {
dataBase = Room.databaseBuilder(
context.applicationContext,
AppDataBase::class.java,
"app_database"
).addMigrations(MIGRATION_1_2,MIGRATION_2_3)
.build()
} finally {
// 计数一次
countDownLatch.countDown()
}
}
}
// 数据库升级:student表 添加一列 phone
private val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// student表 添加一列 phone
var sql = "alter table student add column phone TEXT not null default ''"
database.execSQL(sql)
}
}
// ROOM 是不能降级的,我非要删除一个字段,却要保证数据的稳定性,这个是特殊情况
// 特殊手法降级
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
// 实现删除字段 pwd
// SQL 四步法
// 1.先建立临时表
database.execSQL("create table stu_temp (sid integer primary key not null," +
"name TEXT not null default '',phone TEXT not null default '')")
// 2.复制之前表的数据
database.execSQL("insert into stu_temp(sid,name,phone) select sid,name,phone from student")
// 3.删除student 旧表
database.execSQL("drop table student")
// 4.修改 临时表 为 新表 student
database.execSQL("alter table stu_temp rename to student")
}
}
}
}
- 在Application初始化调用一下init方法初始化数据库。
- getAppDataBase()方法提供实例,对外暴露DAO数据库访问对象。
- 版本升级通过addMigrations方法,并编写要数据库升级要处理的SQL语句。
- 版本从1升级到2时,student表添加了一个字段phone
- 版本从2升级到3时,student表删除了一个字段pwd
四、创建Student仓库,提供外部使用
/**
* Student仓库
*/
object StudentRepository {
private var dao = AppDataBase.getAppDataBase().getStudentDao()
// 这里是提供一份观察数据
var students = queryAll()
// 增
fun insert(vararg students: Student) {
dao.insert(*students)
}
// 删
fun delete(vararg students: Student) {
dao.delete(*students)
}
fun deleteAll() {
dao.deleteAll()
}
// 根据ID删除
fun deleteStudentByIds(vararg sids: Int) {
dao.deleteStudentByIds(*sids)
}
// 改
fun update(vararg students: Student) {
dao.update(*students)
}
// 改
fun update(name: String, sid: Int) {
dao.update(name, sid)
}
// 根据sid 查询一个
fun query(sid: Int): Student {
return dao.query(sid)
}
// 根据sids查询
fun queryAll(vararg sids: Int): List<Student> {
return dao.queryAll(*sids)
}
// 查询所有,与LiveData结合使用
open fun queryAll(): LiveData<List<Student>> {
return dao.queryAll()
}
}
五、结合ViewModel使用
- 布局代码(一个RecyclerView+四个按钮)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.boardour.viewmodel.activity.student.StudentDataBaseViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/ll_bottom"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="@{()->vm.insert()}"
android:text="增" />
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="3dp"
android:layout_weight="1"
android:onClick="@{()->vm.delete()}"
android:text="删" />
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="3dp"
android:layout_weight="1"
android:onClick="@{()->vm.update()}"
android:text="改" />
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="3dp"
android:layout_weight="1"
android:onClick="@{()->vm.query()}"
android:text="查" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- Activity代码
/**
* 数据库测试类
*/
class StudentDataBaseActivity : ViewModelActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// DataBinding
val binding = DataBindingUtil.setContentView<ActivityStudentDatabaseBinding>(
this, R.layout.activity_student_database
)
binding.vm = getViewModel(StudentDataBaseViewModel::class.java)
binding.lifecycleOwner = this
// UI相关
binding.recyclerView.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
val adapter = object : XQuickAdapter<Student>(this, R.layout.item_recycler_database) {
override fun convert(holder: XQuickViewHolder, item: Student, position: Int) {
holder.setText(R.id.name, item.name)
holder.setText(R.id.pwd, item.phone)
holder.setText(R.id.sid, "${item.sid}")
}
}
binding.recyclerView.adapter = adapter
// 观察数据库Student表的变化
StudentRepository.students.observe(this) {
adapter.replaceAll(it)
binding.recyclerView.scrollToPosition(it.size - 1)
}
}
}
open class ViewModelActivity : AppCompatActivity() {
/**
* 子类用
*/
fun <T : ViewModel> getViewModel(clazz: Class<T>): T {
return ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(clazz)
}
/**
* 子类用
*/
fun <T : ViewModel> getAndroidViewModel(clazz: Class<T>): T {
return ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
)
.get(clazz)
}
}
- ViewModel代码
/**
* 结合ViewModel使用
*/
class StudentDataBaseViewModel : ViewModel() {
fun insert() {
// 新增
thread {
// 版本1 StudentRepository.insert(Student("name", "pwd"))
// 版本2 StudentRepository.insert(Student("name", "pwd","phone"))
// 版本3
StudentRepository.insert(Student("name", "1366"))
}
}
fun delete() {
// 删除所有
thread {
StudentRepository.deleteAll()
}
}
fun update() {
thread {
StudentRepository.update("Lven", 6)
}
}
fun query() {
thread {
var stu = StudentRepository.query(6)
if (stu != null) {
Log.e("TAG", stu.name)
}
}
}
}
网友评论