1. 简介
自从Google推出了Room
,我们可以优雅的使用sqlite数据库。
当然用Room
+Paging
的组合,代码写起来飞起来
关于Room
+Paging
的使用方法请参考下面的教程。
Paging在Android中的应用: juejin.cn/post/684490…
在App的build.gradle
的文件中添加Room
库的引用。
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"
implementation "androidx.room:room-testing:$room_version"
implementation "androidx.room:room-ktx:$room_version"
复制代码
2. 创建Entity
首先我们要创建一个data class
作为数据库的Entity class。
然后使用下面介绍的注解添加关于数据库的额外信息。
@Entity
在想创建的entity上面添加@Entity
的注解。
@Entity(tableName = "users_table")
data class User(...)
复制代码
还可以根据需求在@Entity
后面可以添加很多参数。
tableName
, 可以为Table起名字。示例,tableName = "users_table"
。primaryKeys
, 可以为Table指定主键。示例,primaryKeys = {"firstName", "lastName"}
。Index
, 为了加快数据的查询速度吗,可以添加索引。示例,indices = {@Index("lastName")
。unique
,在索引当中需要确保两个列中的信息的唯一性时可以添加。示例indices = {@Index(value = {"first_name", "last_name"},unique = true)}
@PrimaryKey
@PrimaryKey
注解跟上面@Entity
中的primaryKeys
的作用是一样的。
针对自动增加的主键,还可以添加autoGenerate=true
。
@PrimaryKey(autoGenerate = true)
val id: Int? = null,
复制代码
@ColumnInfo
为entity中的属性添加列表名字name="name"
。
@ColumnInfo(name = "birthday")
val birthday: String
复制代码
@Ignore
如果在entity中存在不想存入数据库的属性,可以添加忽略的注解@Ignore
。
@Ignore
val nationality: String
复制代码
@Embedded
如果在Entity类里面如果有其他的对象,可以使用@Embedded
进行嵌套。当然要被嵌套的对象也需要是被@Entity
加上注解的Entity类。
@Embedded
val address: Address
复制代码
3. 创建Dao
@Dao
Dao
的全称是Data Access Object
,是数据访问对象是一个面向对象的数据库接口。在一般开发中需要单独抽象出一个Dao
。
在Room
中需要创建interface,并添加@Dao
注解。
@Dao
interface UserDao {}
复制代码
@Query
需要在实现查询的方法上面添加@Query
,并加上要查询的SQL语句。
@Query("SELECT * FROM users_table where first_name = :firstName")
suspend fun getUserByFirstName(firstName:String): List<User>
复制代码
如果方法中有传入的参数需要在SQL查询语句中使用,可以用:parameter
来绑定参数。
@Insert
要在数据库中插入数据时需要加上@Insert
的注解,但是不需要加上SQL语句。
需要把插入数据库的对象作为参数传入到函数中。
@Insert
suspend fun insertUser(users: List<User>)
复制代码
@Delete
要在数据库中删除数据时需要加上@Delete
的注解,但是不需要加上SQL语句。
需要把删除的对象作为参数传入到函数中。
@Delete
suspend fun deleteUser(users: List<User>)
复制代码
@Update
要在数据库中更新数据时需要加上@Update
的注解,但是不需要加上SQL语句。
需要把更新的对象作为参数传入到函数中。
@Update
public void updateUsers(User... user);
复制代码
@Transaction
可以使用@Transaction
的注解,添加SQLite的事务。
@Transaction
suspend fun deleteAllAndInsertUser(users: List<User>) {
deleteUser(getAll())
insertUser(users)
}
复制代码
整体代码
@Dao
interface UserDao {
@Query("SELECT * FROM users_table")
suspend fun getAll(): List<User>
@Insert
suspend fun insertUser(user: User)
@Update
public void updateUsers(User... user);
@Delete
suspend fun deleteUser(users: List<User>)
@Transaction
suspend fun deleteAllAndInsertUser(users: List<User>) {
deleteUser(getAll())
users.forEach { insertUser(it) }
}
}
复制代码
4. 创建RoomDatabase
示例代码如下:
@Database(version = 1, entities = [User::class]) // 添加Database的注解,还需要添加版本信息和entities信息
@TypeConverters(Converter::class) // 添加TypeConverter的注解
abstract class AppDatabase : RoomDatabase() {
companion object {
private const val DB_NAME = "user_database.db"
private var INSTANCE: AppDatabase? = null
private var lock = Any()
fun getInstance(context: Context): AppDatabase {
synchronized(lock) {
if (INSTANCE == null) {
// 第一个参数是Context, 第二个参数是DB的class,第三个参数是DB名字
INSTANCE =
Room.databaseBuilder(context, AppDatabase::class.java, DB_NAME).build()
}
return INSTANCE!!
}
}
fun destroyInstance() {
INSTANCE = null
}
}
abstract fun getUserDao(): UserDao
}
复制代码
数据库类需要继承RoomDatabase
,同时必须声明的是抽象类
。
还有需要注意的一点是,创建数据库的实例对性能的一样特别大,所以不能重复创建。针对这个问题普遍的做法是把创建实例的方法设计成Singleton模式
。
@Database
需要给数据库的类上面加上@Database
的注解。同时还需要不上下列信息。
version
,指定数据库的版本。entities
,需要指定所有的entity类。
@Database(version = 1, entities = [User::class])
复制代码
@TypeConverter
Room是支持字符串和整型等的基本数据类型,但是遇到其他类型的数据类型时,我们就需要用@TypeConverter
来进行数据类型的转换。比如User里面的生日的属性是Date时,就需要进行数据类型的转换。
class Converter {
@TypeConverter
fun longToDate(timeStamp: Long?): Date? {
if (timeStamp == null) return null
return Date(timeStamp)
}
@TypeConverter
fun dateToLong(date: Date?): Long? {
if (date == null) return null
return date.time
}
}
复制代码
5. 数据库升级,Migration
当App已经发布以后遇到需要更新和改变数据库结构时,就需要用到数据库的数据迁移。Room为我们提供了数据库升级的功能。
// 从版本1升级到版本2
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " +
"PRIMARY KEY(`id`))")
}
}
// 从版本2升级到版本3
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
}
}
// 添加版本升级到Room的builder中
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()
复制代码
6. Github
Github: github.com/HyejeanMOON…
其他教程:
Android的属性动画(Property Animation)详细教程: juejin.cn/post/684490…
Android ConstraintLayout的易懂教程: juejin.cn/post/684490…
在RecyclerView中可以应对多个ViewType的库--Groupie: juejin.cn/post/684490…
Google的MergeAdapter的使用: juejin.cn/post/684490…
Paging在Android中的应用: juejin.cn/post/684490…
Android UI测试之Espresso: juejin.cn/post/684490…
Android WorkManager的使用: juejin.cn/post/684490…