JetPack Room使用

文章内容包括:
  • 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

    // 查询所有,与LiveData结合使用
    @Query("select * from student")
    fun queryAll(): LiveData>

}
三、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 {
        return dao.queryAll(*sids)
    }

    // 查询所有,与LiveData结合使用
    open fun queryAll(): LiveData> {
        return dao.queryAll()
    }

}
五、结合ViewModel使用
  • 布局代码(一个RecyclerView+四个按钮)



    

        
    

    

        

        

            
  • Activity代码
/**
 * 数据库测试类
 */
class StudentDataBaseActivity : ViewModelActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // DataBinding
        val binding = DataBindingUtil.setContentView(
            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(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  getViewModel(clazz: Class): T {
        return ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(clazz)
    }

    /**
     * 子类用
     */
    fun  getAndroidViewModel(clazz: Class): 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使用)