Room with LiveData:适配器中的多对多映射
Room with LiveData: Many to Many mapping in adapter
举个例子:
PRODUCTS 和 ORDERS 存在多对多映射。所以一个产品可以在多个订单上,一个订单可以有多个产品。在 Room 中,我有一个实体,它同时具有产品 ID 和订单 ID 作为外键,因此我可以保存关系。现在很容易获得特定产品的所有订单以及特定订单的所有产品。
麻烦来了。据我所知,无法在 1 query/entity 中获取包含所有产品的订单对象。这可以更详细地阅读 in this post. 在大多数地方,我可以通过 运行 两个查询来绕过它。第一个获取我感兴趣的订单,第二个根据订单的Id获取商品
现在我想在适配器中显示订单与其产品的组合。为此,我需要将我所有的订单与他们的产品结合起来。我对如何使用 LiveData 解决这个问题一无所知。
我认为最好的解决方案是创建一个直接从数据库中获取 OrderWithProducts 的查询。 This post suggests 应该可以,但我没能成功。该示例中最关键的部分也丢失了:OrderItem class.
如果该解决方案不可行,则必须通过某种方式获取包含 2 个查询的 LiveData OrderWithProducts 列表并以某种方式组合它们。
编辑
在@Demigod 的建议之后,现在我的 ViewModel 中有以下内容:
// MediatorLiveData can observe other LiveData objects and react on their emissions.
var liveGroupWithLights = MutableLiveData<List<GroupWithLights>>()
fun createOrdersWithProducts() {
appExecutors.diskIO().execute {
val ordersWithProducts = mutableListOf<OrderWithProducts>()
val orders = orderRepository.getGroupsSync()
for (order in orders) {
val products = productRepository.getProductsSync(order.id)
val orderWithProducts = OrderWithProducts(order, products)
ordersWithProducts.add(orderWithProducts)
}
liveGroupWithLights.postValue(ordersWithProducts)
}
}
我片段中用于向适配器提交数据的函数:
private fun initRecyclerView() {
orderListViewModel.getOrdersWithProducts().observe(this, Observer { result ->
adapter.submitList(result)
})
}
所以现在我可以将 OrderWithProduct 对象作为我的适配器的项目。这太棒了,我可以在我的适配器中为每个订单使用产品。现在,每当数据库中的值发生变化时,我都无法更新这些项目。这部分有什么想法吗?
Edit2:失效追踪器
db.invalidationTracker.addObserver(object : InvalidationTracker.Observer("orders", "products", "order_product_join") {
override fun onInvalidated(tables: MutableSet<String>) {
createOrdersWithProducts()
}
})
我现在遇到的问题是验证跟踪器会收到很多关于单个更改的通知。
据我所知,目前无法使用单个查询。
要解决此问题,您需要在此处 运行 进行多次查询。首先 - 通过单个查询获取订单列表,然后获取每个订单的产品列表。为此,我可以想到几种选择:
- 创建您自己的
OrdersWithProductsProvider
,这将 return 这个组合实体(Order
和 List<Porduct>
),并且它将订阅对 [=14= 的更改] 在每次 orders
或 products
table 更改时使用 LiveData
发出新对象。
- 您可以使用
MediatorLiveData
来用 Product
填充 Order
的列表,但我认为这不是最佳方法,因为您需要运行在后台线程查询,也许这里使用Rx
更方便。
就我个人而言,我会使用第一个选项,因为我可能想获得最新的产品订单列表,这意味着更新应该在三个 table 发生变化时触发( products
、orders
、products_to_orders
),这可以通过 Room.InvalidationTracker
完成。在该提供商内部,我将使用 Rx
(可以通过 LiveDataReactiveStreams
与 LiveData
一起使用)。
关于如何实现的补充:
如何实现这一点并不重要,唯一重要的是 - 运行 后台线程中的整个查询 post 到 LiveData
。您可以使用 Executor
、Rx
或简单的 Thread
。所以它看起来像:
private val database : Database // Get the DB
private val executor = Executors.newSingleThreadExecutor()
private val liveData = MutableLiveData<List<OrderWithProducts>>()
fun observeOrdersWithProducts():LiveData<List<OrderWithProducts>> {
return liveData
}
private fun updateOrdersWithProducts() {
executor.post {
val ordersWithProducts = mutableListOf<OrderWithProducts>()
val orders = db.orders()
for (order : orders) {
val products = database.productsForOrder(order)
val orderWithProducts = OrderWithProducts(order, products)
ordersWithProducts.add(orderWithProducts)
}
liveData.post(ordersWithProducts)
}
}
将其视为不完整的工作代码,而是一个实现示例。
在 initialization/first 调用上调用 updateOrdersWithProducts
,每次 InvalidationTracker
都会通知数据库更改。
举个例子:
PRODUCTS 和 ORDERS 存在多对多映射。所以一个产品可以在多个订单上,一个订单可以有多个产品。在 Room 中,我有一个实体,它同时具有产品 ID 和订单 ID 作为外键,因此我可以保存关系。现在很容易获得特定产品的所有订单以及特定订单的所有产品。
麻烦来了。据我所知,无法在 1 query/entity 中获取包含所有产品的订单对象。这可以更详细地阅读 in this post. 在大多数地方,我可以通过 运行 两个查询来绕过它。第一个获取我感兴趣的订单,第二个根据订单的Id获取商品
现在我想在适配器中显示订单与其产品的组合。为此,我需要将我所有的订单与他们的产品结合起来。我对如何使用 LiveData 解决这个问题一无所知。
我认为最好的解决方案是创建一个直接从数据库中获取 OrderWithProducts 的查询。 This post suggests 应该可以,但我没能成功。该示例中最关键的部分也丢失了:OrderItem class.
如果该解决方案不可行,则必须通过某种方式获取包含 2 个查询的 LiveData OrderWithProducts 列表并以某种方式组合它们。
编辑 在@Demigod 的建议之后,现在我的 ViewModel 中有以下内容:
// MediatorLiveData can observe other LiveData objects and react on their emissions.
var liveGroupWithLights = MutableLiveData<List<GroupWithLights>>()
fun createOrdersWithProducts() {
appExecutors.diskIO().execute {
val ordersWithProducts = mutableListOf<OrderWithProducts>()
val orders = orderRepository.getGroupsSync()
for (order in orders) {
val products = productRepository.getProductsSync(order.id)
val orderWithProducts = OrderWithProducts(order, products)
ordersWithProducts.add(orderWithProducts)
}
liveGroupWithLights.postValue(ordersWithProducts)
}
}
我片段中用于向适配器提交数据的函数:
private fun initRecyclerView() {
orderListViewModel.getOrdersWithProducts().observe(this, Observer { result ->
adapter.submitList(result)
})
}
所以现在我可以将 OrderWithProduct 对象作为我的适配器的项目。这太棒了,我可以在我的适配器中为每个订单使用产品。现在,每当数据库中的值发生变化时,我都无法更新这些项目。这部分有什么想法吗?
Edit2:失效追踪器
db.invalidationTracker.addObserver(object : InvalidationTracker.Observer("orders", "products", "order_product_join") {
override fun onInvalidated(tables: MutableSet<String>) {
createOrdersWithProducts()
}
})
我现在遇到的问题是验证跟踪器会收到很多关于单个更改的通知。
据我所知,目前无法使用单个查询。 要解决此问题,您需要在此处 运行 进行多次查询。首先 - 通过单个查询获取订单列表,然后获取每个订单的产品列表。为此,我可以想到几种选择:
- 创建您自己的
OrdersWithProductsProvider
,这将 return 这个组合实体(Order
和List<Porduct>
),并且它将订阅对 [=14= 的更改] 在每次orders
或products
table 更改时使用LiveData
发出新对象。 - 您可以使用
MediatorLiveData
来用Product
填充Order
的列表,但我认为这不是最佳方法,因为您需要运行在后台线程查询,也许这里使用Rx
更方便。
就我个人而言,我会使用第一个选项,因为我可能想获得最新的产品订单列表,这意味着更新应该在三个 table 发生变化时触发( products
、orders
、products_to_orders
),这可以通过 Room.InvalidationTracker
完成。在该提供商内部,我将使用 Rx
(可以通过 LiveDataReactiveStreams
与 LiveData
一起使用)。
关于如何实现的补充:
如何实现这一点并不重要,唯一重要的是 - 运行 后台线程中的整个查询 post 到 LiveData
。您可以使用 Executor
、Rx
或简单的 Thread
。所以它看起来像:
private val database : Database // Get the DB
private val executor = Executors.newSingleThreadExecutor()
private val liveData = MutableLiveData<List<OrderWithProducts>>()
fun observeOrdersWithProducts():LiveData<List<OrderWithProducts>> {
return liveData
}
private fun updateOrdersWithProducts() {
executor.post {
val ordersWithProducts = mutableListOf<OrderWithProducts>()
val orders = db.orders()
for (order : orders) {
val products = database.productsForOrder(order)
val orderWithProducts = OrderWithProducts(order, products)
ordersWithProducts.add(orderWithProducts)
}
liveData.post(ordersWithProducts)
}
}
将其视为不完整的工作代码,而是一个实现示例。
在 initialization/first 调用上调用 updateOrdersWithProducts
,每次 InvalidationTracker
都会通知数据库更改。