如何使用 Kotlin Flow 手动映射 Room 一对一关系
How to manually map a Room one-to-one relationship using Kotlin Flow
在我的场景中,交易和类别之间存在一对一的关系。
为了查询交易列表和相应的类别,文档告诉我我必须首先通过创建一个 TransactionAndCategory
来模拟两个实体 (https://developer.android.com/training/data-storage/room/relationships) 之间的一对一关系class
我真的不想这样做,它不干净而且在我看来真的是糟糕的编码。
因此,我尝试自己关联这些对象,但我还没有找到方法。这是我尝试做的事情:
class TransactionRepositoryImpl(private val transactionDao: TransactionDao, private val categoryDao: CategoryDao) : TransactionRepository {
override fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable{
Transaction(it, categoryDao.get(it.categoryId))
}
}
}
这会导致错误,因为 categoryDao.get(it.categoryId)
returns 流本身:
@Dao
interface CategoryDao {
@Query("SELECT * FROM categories WHERE uid = :id")
fun get(id: Int): Flow<DatabaseCategory>
}
mapIterable 函数只是 'unwrap' 列表的扩展:
inline fun <T, R> Flow<Iterable<T>>.mapIterable(crossinline transform: (T) -> R): Flow<List<R>> =
map { it.map(transform) }
交易本身是从 TransactionDao
:
中这样检索的
fun getAll(): Flow<List<DatabaseTransaction>>
我的事务域模型的构造函数将两个数据库模型作为参数:
constructor(databaseTransaction: DatabaseTransaction, databaseCategory: DatabaseCategory)
我希望比我拥有更多 Kotlin Flow 经验的人遇到了同样的问题,并且可以向我提供一些 ideas/insights/solutions 如何在不创建 TransactionAndCategory 的情况下以干净的方式 link 这些对象class.
编辑:
我尝试暂停 categoryDao.get 方法:
@Query("SELECT * FROM categories WHERE uid = :id")
suspend fun get(id: Int): DatabaseCategory
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable{
Transaction(it, categoryDao.get(it.categoryId))
}
}
不幸的是,无法在 mapIterable
中调用 categoryDao.get
,所以我仍然坚持这个
编译错误:“暂停函数只能在协程体内调用”
如评论中所述,当您将 Dao#get
更改为 suspend fun Dao#get
时,房间数据库可以 return 您的对象的一个实例。
在此示例中,CategoryDao#get
不应 return Flow,而是对象本身。因为我们不能/不应该在主线程上访问数据库,所以我们使用协程。
将 CategoryDao#get
更改为 suspend fun get(): DatabaseCategory
并进一步将 TransactionRepositoryImpl#getall
更改为 suspend fun getAll
应该可以解决您的问题:)
编辑
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.map { list ->
list.map {
Transaction(it, categoryDao.get(it.categoryId))
}
}
}
或者,如果您想使用 mapIterable 函数,请将其更改为以下内容
inline fun <T, R> Flow<Iterable<T>>.mapIterable(crossinline transform: suspend (T) -> R): Flow<List<R>> =
map { list ->
list.map { item ->
transform(item)
}
}
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable {
Transaction(it, categoryDao.get(it.categoryId))
}
}
在我的场景中,交易和类别之间存在一对一的关系。
为了查询交易列表和相应的类别,文档告诉我我必须首先通过创建一个 TransactionAndCategory
来模拟两个实体 (https://developer.android.com/training/data-storage/room/relationships) 之间的一对一关系class
我真的不想这样做,它不干净而且在我看来真的是糟糕的编码。 因此,我尝试自己关联这些对象,但我还没有找到方法。这是我尝试做的事情:
class TransactionRepositoryImpl(private val transactionDao: TransactionDao, private val categoryDao: CategoryDao) : TransactionRepository {
override fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable{
Transaction(it, categoryDao.get(it.categoryId))
}
}
}
这会导致错误,因为 categoryDao.get(it.categoryId)
returns 流本身:
@Dao
interface CategoryDao {
@Query("SELECT * FROM categories WHERE uid = :id")
fun get(id: Int): Flow<DatabaseCategory>
}
mapIterable 函数只是 'unwrap' 列表的扩展:
inline fun <T, R> Flow<Iterable<T>>.mapIterable(crossinline transform: (T) -> R): Flow<List<R>> =
map { it.map(transform) }
交易本身是从 TransactionDao
:
fun getAll(): Flow<List<DatabaseTransaction>>
我的事务域模型的构造函数将两个数据库模型作为参数:
constructor(databaseTransaction: DatabaseTransaction, databaseCategory: DatabaseCategory)
我希望比我拥有更多 Kotlin Flow 经验的人遇到了同样的问题,并且可以向我提供一些 ideas/insights/solutions 如何在不创建 TransactionAndCategory 的情况下以干净的方式 link 这些对象class.
编辑: 我尝试暂停 categoryDao.get 方法:
@Query("SELECT * FROM categories WHERE uid = :id")
suspend fun get(id: Int): DatabaseCategory
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable{
Transaction(it, categoryDao.get(it.categoryId))
}
}
不幸的是,无法在 mapIterable
中调用 categoryDao.get
,所以我仍然坚持这个
编译错误:“暂停函数只能在协程体内调用”
如评论中所述,当您将 Dao#get
更改为 suspend fun Dao#get
时,房间数据库可以 return 您的对象的一个实例。
在此示例中,CategoryDao#get
不应 return Flow,而是对象本身。因为我们不能/不应该在主线程上访问数据库,所以我们使用协程。
将 CategoryDao#get
更改为 suspend fun get(): DatabaseCategory
并进一步将 TransactionRepositoryImpl#getall
更改为 suspend fun getAll
应该可以解决您的问题:)
编辑
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.map { list ->
list.map {
Transaction(it, categoryDao.get(it.categoryId))
}
}
}
或者,如果您想使用 mapIterable 函数,请将其更改为以下内容
inline fun <T, R> Flow<Iterable<T>>.mapIterable(crossinline transform: suspend (T) -> R): Flow<List<R>> =
map { list ->
list.map { item ->
transform(item)
}
}
override suspend fun getAll(): Flow<List<Transaction>> {
return transactionDao
.getAll()
.mapIterable {
Transaction(it, categoryDao.get(it.categoryId))
}
}