Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。具体来说,Room 具有以下优势:
- 针对 SQL 查询的编译时验证。
- 可最大限度减少重复和容易出错的样板代码的方便注解。
- 简化了数据库迁移路径。
step1:app/build.gradle
中引入room
implementation "androidx.room:room-ktx:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
step2:创建实体类
这个类就是要存储的数据结构,通过@Entity(tableName="test_table")
定义并指定表名。类中的每个属性代表表中的一列。Room 最终会使用这些属性来创建表并将数据库行中的对象实例化
//声明数据表,并且指定数据表名称
@Entity(tableName = "test_table")
data class TestEntity(@PrimaryKey(autoGenerate = true) val id: Int, @ColumnInfo(name = "name")val name:String)
//如果列名也叫做word可以省略@ColumnInfo
step3:创建DAO
其实就是提供对数据表的增删改查。在 DAO(数据访问对象)中,您可以指定 SQL 查询并将其与方法调用相关联。编译器会检查 SQL 并根据常见查询的方便的注解(如 @Insert
)生成查询。Room 会使用 DAO 为代码创建整洁的 API。DAO 必须是一个接口或抽象类。默认情况下,所有查询都必须在单独的线程上执行。Room 支持 Kotlin 协程,您可使用 suspend
修饰符对查询进行注解,然后从协程或其他挂起函数对其进行调用。
@Dao
interface TestDao {
@Query("SELECT * FROM test_table ORDER BY word ASC")
fun getSavedEntities(): Flow<List<TestEntity>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(entity: TestEntity)
@Query("DELETE FROM test_table")
suspend fun deleteAll()
}
- TestDao 是一个接口,DAO 必须是接口或抽象类。
-
@Dao
注解将其标识为 Room 的 DAO 类。 -
suspend fun insert(word: TestEntity)
:声明挂起函数以插入一个字词。 -
@Insert
注解是一种特殊的 DAO 方法注解,使用 DAO 方法注解时,您无需提供任何 SQL!(还有用于删除和更新行的@Delete
和@Update
注解) -
onConflict = OnConflictStrategy.IGNORE
:所选 onConflict 策略将忽略与列表中的现有字词完全相同的新字词。如需详细了解可用的冲突策略,请参阅相关文档。 -
suspend fun deleteAll()
:声明挂起函数以删除所有字词。 - 没有便于删除多个实体的注解,因此实体中要带有通用
@Query
注解。 -
@Query
("DELETE FROM word_table"):@Query
要求您将 SQL 查询作为字符串参数提供给注解,以执行复杂的读取查询及其他操作。 - fun getSavedEntities(): 使用 Flow 封装返回的 List。Flow 是值的异步序列。Flow 通过异步操作(例如网络请求、数据库调用或其他异步代码),一次生成一个值(而不是一次生成所有值)。它的 API 支持协程,因此也可以使用协程来完善 Flow。
- @Query("SELECT * test_table ORDER BY word ASC"):可返回按升序排序的字词列表的查询。
step4:实现room数据库。
Room 数据库类必须是抽象且必须扩展 RoomDatabase。整个应用通常只需要一个 Room 数据库实例。
@Database(entities = arrayOf(TestEntity::class), version = 1, exportSchema = false)
public abstract class TestRoomDatabase : RoomDatabase() {
abstract fun wordDao(): TestDao
companion object {
@Volatile
private var INSTANCE: TestRoomDatabase? = null
fun getDatabase(context: Context): TestRoomDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
TestRoomDatabase::class.java,
"test_database"
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
- Room 数据库类必须是 abstract 且扩展
RoomDatabase
. - 您可以通过
@Database
将该类注解为 Room 数据库,并使用注解参数声明数据库中的实体以及设置版本号。每个实体都对应一个将在数据库中创建的表。数据库迁移这里不介绍了,因此这里已将 exportSchema 设置为 false 以免显示 build 警告。在真实的应用中,可以考虑为 Room 设置一个用于导出架构的目录,以便将当前架构签入版本控制系统。 - 数据库会通过每个 @Dao 的抽象“getter”方法公开 DAO。
- 定义了一个单例TestRoomDatabase,,以防出现同时打开数据库的多个实例的情况。
- getDatabase 会返回该单例。首次使用时,它会创建数据库,具体方法是:使用 Room 的数据库构建器在 TestRoomDatabase 类的应用上下文中创建 RoomDatabase 对象,并将其命名为 "word_database"。
step5:实在Repository中使用DAO
class TestRepository(private val testDao: TestDao) {
val allEntities: Flow<List<TestEntity>> = testDao.getSavedEntities()
@WorkerThread
suspend fun insert(entity: TestEntity) {
testDao.insert(entity)
}
}
step6:实在ViewModel中使用Repository
这里利用了Hilt。首先在viewModel中查询所有结果,并且将flow转化为liveData,在需要的activity中注册监听。当数据库数据发生变化时,监听器就能收到相应的更改数据,比如调用insert之后
@HiltViewModel
class TestViewModel @Inject constructor(private val repository: TestRepository) :ViewModel() {
val allWords: LiveData<List<Word>> = repository.allEntities.asLiveData()
fun insert(entity: TestEntity) = viewModelScope.launch {
repository.insert(entity)
}
}
以上就是Room的简单使用。
网友评论