赞
踩
本文转自Android架构师必学——Jetpack Room数据库应用与源码解析,个人感觉含金量很高。
Room是Google官方在SQLite基础上封装的一款数据持久库,是Jetpack全家桶的一员,和Jetpack其他库有着可以高度搭配协调的天然优势。Room使用APT技术,大大简化了使用SQLite的代码量,只需使用注解配合少量代码即可实现高效的数据库操作。
相对于SQLiteOpenHelper
等传统方法,使用Room操作SQLite有以下优势:
dependencies {
implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
}
Room的使用,主要涉及以下3个组件
这三个组件的概念也出现在其他ORM框架中,有过使用经验的同学理解起来并不困难: 通过Database获取DAO,然后通过DAO查询并获取entities,最终通过entities对数据库table中数据进行读写
Database是我们访问底层数据库的入口,管理着真正的数据库文件。我们使用@Database定义一个Database类:
派生自RoomDatabase
关联其内部数据库table对应的entities
提供获取DAO的抽象方法,且不能有参数
@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
运行时,我们可以通过Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获取Database实例
val db = Room.databaseBuilder(
applicationContext,
UserDatabase::class.java, "users-db"
).build()
创建Databsse的成本较高,推荐使用单例的Database,避免反复创建实例带来的开销
一个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?
)
所有的属性必须是public、或者有get、set方法
属性中至少有一个主键,使用@PrimaryKey表示单个主键,也可以像下面这样定义多主键
@Entity(primaryKeys = arrayOf("firstName", "lastName"))
当主键值为null时,autoGenerate可以帮助自动生成键值
@PrimaryKey(autoGenerate = true) val uid : Int
默认情况下使用类名作为数据库table名,也可使用tableName指定
@Entity(tableName = "users")
Entity中的所有属性都会被持久化到数据库,除非使用@Ignore
@Ignore val picture: Bitmap?
可以使用indices指定数据库索引,unique设置其为唯一索引
@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
@Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
unique = true)))
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)
}
DAO的方法调用都在当前线程进行,所以要避免在UI线程直接访问
有时,需要将自定义类型的数据持久化到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()
}
}
在声明Database时,指定此Converters
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Room中使用Data Access Objects(DAO)对数据库进行读写,相对于SQL语句直接查询,DAO可以定义更加友好的API。DAO中可以自定义CURD方法,还可以方便地与RxJava、LiveData等进行集成。
我们可以使用接口或者抽象类定一个DAO,如果使用抽象类,可以选择性的为其定义构造函数,并接受Database作为唯一参数。
Room在编译期会基于定义的DAO生成具体实现类,实现具体CURD方法。
@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>)
}
onConflict
设置当事务中遇到冲突时的策略
最新代码中ROLLBACK 和 FAIL 已经deprecated了,使用ABORT替代
@Update注解定义更新操作,根据参数对象的主键更新指定row的数据
@Dao
interface UserDao {
@Update(onConflict = OnConflictStrategy.REPLACE)
fun updateUsers(vararg users: User)
@Update fun update(user: User)
}
@Delete定义删除操作,根据主键删除指定row
@Dao
interface UserDao {
@Delete
fun deleteUsers(vararg users: User)
}
@Query注解定义查询操作。@Query中的SQL语句以及返回值类型等会在编译期进行检查,更早的暴露问题
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun loadAllUsers(): Array<User>
}
可以用参数指定@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>
}
返回的结果可以是所有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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。