如何使用 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))
        }
}