美文网首页
JetPack Room使用

JetPack Room使用

作者: Sky_Blue | 来源:发表于2022-05-19 16:23 被阅读0次
    文章内容包括:
    • 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有以下优势:

    1. 编译期的SQL语法检查
    2. 开发高效,避免大量模板代码
    3. API设计友好,容易理解
    4. 可以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)
                }
            }
        }
    }
    
    最终测试结果
    测试结果.png

    相关文章

      网友评论

          本文标题:JetPack Room使用

          本文链接:https://www.haomeiwen.com/subject/iwttprtx.html