在房间数据库 Android 中返回带有嵌套关系的空数据

Posted

技术标签:

【中文标题】在房间数据库 Android 中返回带有嵌套关系的空数据【英文标题】:Null Data Returned with Nested Relation in Room Database Android 【发布时间】:2021-11-02 19:03:22 【问题描述】:

鉴于我有 3 个实体,Order 包含 LineItem 的列表,每个 LineItem 将通过 productId 与一个 Product 关联。

从OrderDao获取数据时,product字段返回null,但lineItem字段有数据的问题。虽然我可以使用 ProductWithLineItem 数据。 已经尝试了很多解决方法,但它不起作用。 这是我的实体和 dao 代码

实体

@Entity(tableName = DataConstant.ORDER_TABLE)
data class Order(
    @PrimaryKey
    @ColumnInfo(name = "orderId")
    val id: String,

    @ColumnInfo(name = "status")
    var status: String
)
@Entity(tableName = DataConstant.LINE_ITEM_TABLE)
data class LineItem(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "lineItemId")
    val id: Long,

    @ColumnInfo(name = "productId")
    val productId: String,

    @ColumnInfo(name = "orderId")
    val orderId: String,

    @ColumnInfo(name = "quantity")
    var quantity: Int,

    @ColumnInfo(name = "subtotal")
    var subtotal: Double
) 

@Entity(tableName = DataConstant.PRODUCT_TABLE)
data class Product(
    @PrimaryKey
    @NonNull
    @ColumnInfo(name = "productId")
    val id: String,

    @ColumnInfo(name = "name")
    var name: String?,

    @ColumnInfo(name = "description")
    var description: String?,

    @ColumnInfo(name = "price")
    var price: Double?,

    @ColumnInfo(name = "image")
    var image: String?,

    )

关系 POJO

data class ProductAndLineItem(
    @Embedded val lineItem: LineItem?,
    @Relation(
        parentColumn = "productId",
        entityColumn = "productId"
    )
    val product: Product?
)

data class OrderWithLineItems(
    @Embedded var order: Order,
    @Relation(
        parentColumn = "orderId",
        entityColumn = "orderId",
        entity = LineItem::class
    )
    val lineItemList: List<ProductAndLineItem>
)


@Dao
interface OrderDao 
    @Transaction
    @Query("SELECT * FROM `$DataConstant.ORDER_TABLE` WHERE orderId = :id")
    fun getById(id: String): Flow<OrderWithLineItems>

Dao运行后的结果

Result after running query

【问题讨论】:

【参考方案1】:

这是我的实体和 dao 代码

你的代码看起来很好,除了返回一个流,测试,使用你的代码,但是在主线程上使用 List(并且没有 WHERE 子句),即 Dao 是:-

@Query("SELECT * FROM $DataConstant.ORDER_TABLE")
@Transaction
abstract fun getOrderWithLineItemsAndWithProduct(): List<OrderWithLineItems>

结果:-

正在加载/测试的数据使用:-

    db = TheDatabase.getInstance(this)
    orderDao = db.getOrderDao()
    orderDao.clearAll()

    orderDao.insert(Product("product1","P1","desc1",10.01,"image1"))
    orderDao.insert(Product("product2","P2","desc2",10.02,"image2"))
    orderDao.insert(Product("product3","P3","desc3",10.03,"image3"))
    orderDao.insert(Product("product4","P4","desc4",10.04,"image4"))
    orderDao.insert(Product("","","",0.0,""))

    val o1 = orderDao.insert(Order("Order1","initiaited"))
    val o2 = orderDao.insert(Order("Order2","finalised")) // Empty aka no List Items

    val o1l1 = orderDao.insert(LineItem(10,"product3","Order1",1,10.01))
    val o1l2 = orderDao.insert(LineItem(20,"product4","Order1",2,20.08))
    val o1l3 = orderDao.insert(LineItem(30,"","Order1",3,30.09))
    val o1l4 = orderDao.insert(LineItem(40,"","x",1,10.01))
    //val o1l3 = orderDao.insert(LineItem(30,"no such product id","Order1",10,0.0))
    // exception whilst trying to extract if not commented out at test = ....
    val TAG = "ORDERINFO"
    val test = orderDao.getOrderWithLineItemsAndWithProduct()

    for(owl: OrderWithLineItems in orderDao.getOrderWithLineItemsAndWithProduct()) 
        Log.d(TAG,"Order is $owl.order.id status is $owl.order.status")
        for(pal: ProductAndLineItem in owl.lineItemList) 
            Log.d(TAG,"\tLine Item is $pal.lineItem.id " +
                    "for Order $pal.lineItem.orderId " +
                    "for ProductID $pal.lineItem.productId " +
                    "Quantity=$pal.lineItem.quantity " +
                    "Product description is $pal.product.description Product Image is $pal.product.image Price is $pal.product.price")
        
    

因此,我认为问题可能是由于某种原因,Flow 正在检测第一个查询何时完成但在基础查询之前。

也就是说,当使用@Relation 时,核心对象(订单)通过查询提取并创建核心对象,然后相关对象由另一个查询提取并用于将所有相关对象构建为列表(除非只是一个当它不必是一个列表时)。因此,在此基础查询之前,核心对象将具有基础对象的 null 或空列表。当然,对于@Relations 的层次结构,它会沿着/向下复制层次结构。

我建议暂时将.allowMainThreadQueires 添加到databaseBuilder 并使用List&lt;OrderWithLineItems&gt; 或仅使用一个OrderWithLineItems。如果使用它,那么您将获得产品,那么问题在于流程(这是我怀疑的)。

【讨论】:

尚未尝试过,但感谢@MikeT 对此案例的深入解释。关于性能,在主线程上查询可能不好,这种情况有什么优化的方法吗? 已经解决了这个问题。问题是因为我在编写测试时忘记插入产品数据。 Flow 运行良好,没有问题。再次感谢@MikeT 的回答

以上是关于在房间数据库 Android 中返回带有嵌套关系的空数据的主要内容,如果未能解决你的问题,请参考以下文章

带有 RxJava Single 的 Android Livedata 在房间数据库中不起作用

房间持久性库 - 带有 List<Video> 的嵌套对象,@Embedded 不起作用。

使用 Android 房间数据库在同一张表中的一对多关系

带有 Android Studio 的房间数据库

Android 房间查询返回 null

具有多个 parentColumn 和 entityColumn 的 Android 房间关系