当前位置:   article > 正文

Android架构师必学——Jetpack Room数据库应用与源码解析(转载)_onconflictstrategy.none

onconflictstrategy.none

前言

本文转自Android架构师必学——Jetpack Room数据库应用与源码解析,个人感觉含金量很高。

Room是Google官方在SQLite基础上封装的一款数据持久库,是Jetpack全家桶的一员,和Jetpack其他库有着可以高度搭配协调的天然优势。Room使用APT技术,大大简化了使用SQLite的代码量,只需使用注解配合少量代码即可实现高效的数据库操作。

Room介绍

  1. Room是一个OM(Object Mapping对象映射)数据库,可以方便地在Android应用程序上访问数据库。
  2. Room抽象了SQLite,通过提供方便的api来查询数据库,并在编译时验证。并且可以使用SQLite的全部功能,同时拥有Java SQL查询生成器提供的类型安全。

Room的构成

  1. Database:数据库扩展了RoomDatabase的抽象类。可以通过Room获得它的一个实例。databaseBuilder或Room.inMemoryDatabaseBuilder。
  2. Entity:代表一个表结构。
  3. Dao:数据访问对象是Room的主要组件,负责定义访问数据库的方法。

优点:

  • Google官方库,和Jetpack其他库(比如Lifecycle,LiveData)等有天然的融合搭配使用优势。
  • 在编译器可以对SQL语法进行检查。
  • 使用APT,简单地几个注解搭配少量代码即可使用,大量减少模板代码。
  • 查询代码自定义,可以实现更复杂的查询功能,也可以对SQL语句进行优化。
  • 简化数据库的迁移路径。

不足:

  • 查询时必须手动写SQL语句,不提供默认的查询配置。
  • 效率比其他数据库框架(GreenDao等)并没有多少提高。
  • 数据库版本升级稍显复杂。

一、基本介绍

框架特点

相对于SQLiteOpenHelper等传统方法,使用Room操作SQLite有以下优势:

  • 编译期的SQL语法检查
  • 开发高效,避免大量模板代码
  • API设计友好,容易理解
  • 可以与RxJava、 LiveData 、 Kotlin Coroutines等进行桥接

添加依赖

dependencies {
  implementation "androidx.room:room-runtime:2.2.5"
  kapt "androidx.room:room-compiler:2.2.5"
}
  • 1
  • 2
  • 3
  • 4

1. 基本组件

Room的使用,主要涉及以下3个组件

  • Database: 访问底层数据库的入口
  • Entity: 代表数据库中的表(table),一般用注解
  • Data Access Object (DAO): 数据库访问者

这三个组件的概念也出现在其他ORM框架中,有过使用经验的同学理解起来并不困难: 通过Database获取DAO,然后通过DAO查询并获取entities,最终通过entities对数据库table中数据进行读写

Database

Database是我们访问底层数据库的入口,管理着真正的数据库文件。我们使用@Database定义一个Database类:

  • 派生自RoomDatabase

  • 关联其内部数据库table对应的entities

  • 提供获取DAO的抽象方法,且不能有参数

      @Database(entities = arrayOf(User::class), version = 1)
      abstract class UserDatabase : RoomDatabase() {
        abstract fun userDao(): UserDao
      }
    
    • 1
    • 2
    • 3
    • 4

运行时,我们可以通过Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获取Database实例

val db = Room.databaseBuilder(
    applicationContext,
    UserDatabase::class.java, "users-db"
    ).build()
  • 1
  • 2
  • 3
  • 4

创建Databsse的成本较高,推荐使用单例的Database,避免反复创建实例带来的开销

Entity

一个Entity代表数据库中的一张表(table)。我们使用@Entity定义一个Entiry类,类中的属性对应表中的Column

@Entity
data class User(
  @PrimaryKey val uid: Int,
  @ColumnInfo(name = "first_name") val firstName: String?,
  @ColumnInfo(name = "last_name") val lastName: String?
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 所有的属性必须是public、或者有get、set方法

  • 属性中至少有一个主键,使用@PrimaryKey表示单个主键,也可以像下面这样定义多主键

      @Entity(primaryKeys = arrayOf("firstName", "lastName"))
    
    • 1
  • 当主键值为null时,autoGenerate可以帮助自动生成键值

      @PrimaryKey(autoGenerate = true)  val uid : Int
    
    • 1
  • 默认情况下使用类名作为数据库table名,也可使用tableName指定

      @Entity(tableName = "users")
    
    • 1
  • Entity中的所有属性都会被持久化到数据库,除非使用@Ignore

      @Ignore val picture: Bitmap?
    
    • 1
  • 可以使用indices指定数据库索引,unique设置其为唯一索引

      @Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
      ​
      @Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
              unique = true)))
    
    • 1
    • 2
    • 3
    • 4

Data Access Object (DAO)

DAO提供了访问DB的API,我们使用@Dao定义DAO类,使用@Query@Insert@Update@Delete定义CRUD方法

@Dao
interface UserDao {
  @Query("SELECT * FROM user")
  fun getAll(): List<User>
  
  @Query("SELECT * FROM user WHERE uid IN (:userIds)")
  fun loadAllByIds(userIds: IntArray): List<User>
  
  @Insert
  fun insertAll(vararg users: User)
  
  @Delete
  fun delete(user: User)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

DAO的方法调用都在当前线程进行,所以要避免在UI线程直接访问

Type Converters

有时,需要将自定义类型的数据持久化到DB,此时需要借助Converters进行转换

class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
  return value?.let { Date(it) }
  }
​
  @TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
  return date?.time?.toLong()
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在声明Database时,指定此Converters

@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class UserDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
  • 1
  • 2
  • 3
  • 4
  • 5

2. Data Access Objects(DAO)

Room中使用Data Access Objects(DAO)对数据库进行读写,相对于SQL语句直接查询,DAO可以定义更加友好的API。DAO中可以自定义CURD方法,还可以方便地与RxJava、LiveData等进行集成。

我们可以使用接口或者抽象类定一个DAO,如果使用抽象类,可以选择性的为其定义构造函数,并接受Database作为唯一参数。

Room在编译期会基于定义的DAO生成具体实现类,实现具体CURD方法。

@Insert 插入

@Insert注解插入操作,编译期生成的代码会将所有的参数以单独的事务更新到DB。

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)   
    @Insert fun insertBothUsers(user1: User, user2: User)   
    @Insert fun insertUsersAndFriends(user: User, friends: List<User>)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

onConflict设置当事务中遇到冲突时的策略

  • OnConflictStrategy.REPLACE : 替换旧值,继续当前事务
  • OnConflictStrategy.ROLLBACK : 回滚当前事务
  • OnConflictStrategy.ABORT : 结束当前事务、回滚
  • OnConflictStrategy.FAIL : 当前事务失败、回滚
  • OnConflictStrategy.NONE : 忽略冲突,继续当前事务

最新代码中ROLLBACK 和 FAIL 已经deprecated了,使用ABORT替代

@Update 更新

@Update注解定义更新操作,根据参数对象的主键更新指定row的数据

@Dao
interface UserDao {
    @Update(onConflict = OnConflictStrategy.REPLACE)
    fun updateUsers(vararg users: User)   
    @Update fun update(user: User)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

@Delete 删除

@Delete定义删除操作,根据主键删除指定row

@Dao
interface UserDao {
    @Delete
    fun deleteUsers(vararg users: User)
}
  • 1
  • 2
  • 3
  • 4
  • 5

@Query 查询

@Query注解定义查询操作。@Query中的SQL语句以及返回值类型等会在编译期进行检查,更早的暴露问题

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun loadAllUsers(): Array<User>
}
  • 1
  • 2
  • 3
  • 4
  • 5

指定参数

可以用参数指定@Query中的where条件:

@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE age BETWEEN :minAge AND :maxAge")
    fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>
​
    @Query("SELECT * FROM users WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")
    fun findUserWithName(search: String): List<User>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

返回子集

返回的结果可以是所有column的子集:

data class NameTuple(
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

@Dao
interface UserDao {
    @Query("SELECT first_name, last_name FROM users")
    fun loadFull
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/253988
推荐阅读
相关标签
  

闽ICP备14008679号