您当前的位置: 首页 >  数据库
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Jetpack Room数据库(一)快速上手

沙漠一只雕得儿得儿 发布时间:2021-01-03 11:52:13 ,浏览量:0

目录

一、基础知识

二、详细见进阶使用

三、快速上手

​​​​​​​

一、基础知识

1、依赖库

    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-common:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt "androidx.room:room-compiler:2.2.5"

2、特性

  1. SQL语句高亮(封装原始的sqlite)

  2. 简单入门,依赖库小(相比greenDao、ObjectBox、Realm等)

  3. 功能强大

  4. 数据库监听

  5. 支持Kotlin协程/RxJava/Guava

不足:性能对比其他优秀数据库框架,并无优势;对于ORM的支持不是很好

3、常用注解

  • @Entity 修饰 数据表实体类(内部至少有一个primaryKey标记的字段)

  • @Database 数据库的抽象类

  • @Dao 用于操作数据表的Dao接口

    • @insert 新增

    • @update

    • @delete

    • @query --- sqlite

其他注解:

  1. @ignore 注解忽略对应字段,在数据表中的映射

  2. @index 索引字段标记

  3. @primaryKey 数据表的主键

  4. @ForeignKey 数据表的外键,确定entry表之间关系

  5. @ColumnInfo 用于数据表字段的映射定义

  6. @Embedded 用于数据表字段为外部对象的修饰,不需要那个对象也是@entity

  7. @Relation 用于多表关联

  8. @Transaction 用于数据库的事务操作标记

  9. @DatabaseView 创建虚拟数据表,而非数据库中实际的表数据,可以是多个表的部分拼接的,提高复用

  10. @TypeConverter 用于适配转换

二、详细见进阶使用​​​​​​​

数据库设计三大基本原则(三大范式)

  1. 每列的原子性

  2. 每列均与主键相关

  3. 每列均与主键直接相关

  • 嵌套类

@embedded,将额外对象的内容字段,添加到当前entry的表内,所以,其字段不可与当前entry字段重复

  • 多表联查

  1. sql语句返回数据,构建临时bean对象

  2. databaseView需要在dataBase的抽象类中@database的views添加,而后可用于在@query中使用

  • 升降级

创建migration-->内部处理数据库迁移逻辑

addMigrations,可多个版本迁移处理

附:注意知识点

  1. entry必须非private构造函数,字段不能private

  2. 默认不允许主线程操作,可手动添加allowMainThreadQueries

  3. 使用room结合liveData时候,返回数据可能null

    1. @embeded的挂载,字段避免重复

    2. 创建Dao可以是interface,也可以abstract class ,但是抽象类的时候,所有函数都必须是抽象函数

    3. AS中 java(generated)目录下,可查看插件生成的java代码。dao的操作都是事务性的。

    4. 外键的使用,注意索引index的优化

三、快速上手

demo如下,简单的增、删、改、查操作,数据都是写死的,删除只能点一次

Room数据库主要由三个部分组成:

  • Entity: 数据实体,一个Entity代表一张数据表
  • DAO: 在这里定义数据表的操作方法
  • Database: 数据库

步骤一:使用@Entity创建表

要点1:在类上方添加@Entitiy注解,并定义数据表名称 要点2:通过@PrimaryKey来定义主键,@ColumnInfo来定义列名称,也可以不添加注解@ColumnInfo,那么列名称就默认是属性的名称。

@Entity(tableName = "db_user") //room数据库的注解标记,数据表entity  (tableName="db_user",indices = {@Index(value = "uname",unique = true)})
class DbUser {
    @PrimaryKey(autoGenerate = true)
    var uid = 0

    @ColumnInfo(name = "uname")
    var name: String? = null

    var city: String? = null

    var age = 0

    //如此数据表中不会有@Ignore标记的属性字段
    @Ignore
    var isSingle = false

    override fun toString(): String {
        return "DbUser{" +
                "uid=" + uid +
                ", name='" + name + '\'' +
                ", city='" + city + '\'' +
                ", age=" + age +
                ", single=" + isSingle +
                '}'
    }
}

步骤二、使用@Dao,创建操作数据库的接口,定义数据表操作方法

可以看到,在这里通过各种注解实现了数据表的各种增删改查方法。其中delete,并不是说要传入一个完全一样的实体,只要PrimaryKey匹配就行。具体就不多讲了,一看就懂。

最重要的是要在接口前面添加注解@Dao

@Dao
interface UserDao {
    //查询所有数据,若返回liveData则为 LiveData
    @Query(value = "select * from db_user")
    fun getAll(): List?

    @Query("SELECT * FROM db_user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray?): List? //根据uid查询

    @Query(
        "SELECT * FROM db_user WHERE uname LIKE :name AND "
                + "age LIKE :age LIMIT 1"
    )
    fun findByName(name: String?, age: Int): DbUser?

    @Query("select * from db_user where uid like :id")
    fun getUserById(id: Int): DbUser?

    @Insert
    fun insertAll(vararg users: DbUser?) //支持可变参数

    @Delete
    fun delete(user: DbUser?) //删除指定的user

    @Update(onConflict = OnConflictStrategy.REPLACE)
    fun update(user: DbUser?) //更新,若出现冲突,则使用替换策略,还有其他策略可选择
}

步骤三、创建数据库,搞一个单例类,获取到数据库对象

最重要的当然是要在类前面添加注解@Database,在entities中添加所有数据表的类名,version表示数据库版本,exportSchema表示是否生成数据库结构文件,具体可以参考: 处理Schema

要点1. 自定义的Database类必须继承RoomDatabase。 要点2. 在Database中定义获取数据表操作实例的方法。 要点3. 谷歌建议把Database实例定义成单例模式,因为数据库的初始化消耗量相当大,而且也没必要在一个进程中创建多个数据库实例。

@Database(entities = [DbUser::class], version = 1, exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
    abstract val userDao: UserDao?

    companion object {
        const val DB_NAME = "user.db"
        private var instance: UserDatabase? = null

        @Synchronized
        fun getInstance(context: Context?): UserDatabase? {
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context!!,
                    UserDatabase::class.java,
                    DB_NAME
                )
                    .allowMainThreadQueries() //默认room不允许在主线程操作数据库,这里设置允许
                    .build()
            }
            return instance
        }
    }
}

使用:

class MainActivity : AppCompatActivity() {
    private val tvInsert: TextView by lazy { findViewById(R.id.tv_insert_room) }
    private val tvDelete: TextView by lazy { findViewById(R.id.tv_delete_room) }
    private val tvUpdate: TextView by lazy { findViewById(R.id.tv_update_room) }
    private val tvQueryID: TextView by lazy { findViewById(R.id.tv_query_id_room) }
    private val tvSize: TextView by lazy { findViewById(R.id.tv_size_room) }
    private val tvAll: TextView by lazy { findViewById(R.id.tv_all_room) }


    private var instance: UserDatabase? = null
    private var userDao: UserDao? = null


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        instance = UserDatabase.getInstance(this)
         TODO: 2020/8/29  删除数据库,在测试的时候,清空表,重新建
        deleteDatabase(UserDatabase.DB_NAME)
        userDao = instance?.userDao

    }


    fun insert(view: View?) {
        val sb = StringBuilder()
        var user: DbUser
        for (i in 0..4) {
            user = DbUser()
            user.age = 20 + i
            user.city = "北京 $i"
            user.name = "小明 $i"
            user.isSingle = i % 2 == 0
            userDao!!.insertAll(user)
            sb.append(user.toString()).append("\n")
        }
        //        userDao.insertAll(users.get(0),users.get(1));
        tvInsert.text = sb.toString()
        getAll()
    }

    private fun getAll() {
        val all = userDao!!.getAll() ?: return
        val sb = StringBuilder()
        all.forEach { user ->
            sb.append("uid: ")
                .append(user?.uid)
                .append("姓名: ")
                .append(user?.name)
                .append("年龄: ")
                .append(user?.age)
                .append("城市: ")
                .append(user?.city)
                .append("Single: ")
                .append(user?.isSingle)
                .append("\n")
        }
        val text = "All Size : " + all.size
        tvSize.text = text
        tvAll.text = sb.toString()
    }

    fun delete(view: View?) {
        val user = userDao!!.findByName("小明 " + 3, 23)
        userDao!!.delete(user)
        //
        tvDelete.text = user.toString()
        getAll()
    }

    fun update(view: View?) {
        val user = userDao!!.findByName("小明 " + 2, 22) ?: return
        user.age = 33
        user.city = "上海"
        user.name = "张三"
        user.isSingle = true
        userDao!!.update(user)
        tvUpdate.text = user.toString()
        getAll()
    }

    fun queryId(view: View?) {
        val userById = userDao!!.getUserById(3)
        if (userById != null) {
            tvQueryID.text = userById.toString()
        } else {
            Toast.makeText(this, "id=3 的user查询不到", Toast.LENGTH_SHORT).show()
        }
        //为了显示操作后的更新数据
        getAll()
    }

    fun queryAll(view: View?) {
        getAll()
    }


    override fun onDestroy() {
        super.onDestroy()
        instance!!.clearAllTables()
    }

}

 

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0387s