I have two tables Sale
and SaleProducts
with a one-to-many relationship (one Sale
to many SaleProducts
)
I'm trying to figure out what's the best way to get the sale and all the sale items from the room database using hilt and viewmodels.
Right now I get the LiveData
of List<Sale>
and observe
it. I was thinking of then getting each sales SaleProduct's by looping in the observer
but I that would require me to observe each List<SaleProduct>
.
What's the best way to tackle this?
Room can retrieve the Children of a Parent, with the Parent so there would be no need to loop through getting the children.
To let Room do it on your behalf you could:-
Create a POJO with a field for the Sale
object embedded using the @Embedded
annotation and a field for a List<SaleProducts>
annotated with the @Relationship
annotation, specifying the parent column as the column that is referenced (typically the primary key of the referenced object, the Sale) by the child (The SaleProducts) and with the entity column as the column in the child that references the parent (The Sale).
add a Dao Query, that is:-
@Transaction
annotation, and
@Query
annotation that queries the required Sale objects, ande.g.
Say you have the Sale class as:-
@Entity
data class Sale(
@PrimaryKey
val saleId: Long?=null,/* THE COLUMN REFERENCED BY THE SALEPRRODUCTS */
val saleDate: Long,
/* .... othere columns/fields */
)
And the SaleProducts class as:-
@Entity
data class SaleProducts(
@PrimaryKey
val saleProductId: Long?=null,
@ColumnInfo(index = true) /* Optional BUT suggested else warning will be issued re full table scan */
val saleIdMap: Long, /* THE COLUMN THAT REFERENCES THE PARENT SALE */
/* .... other columns/fields*/
)
And the POJO :-
data class SaleWithSaleProducts(
@Embedded
val sale: Sale,
@Relation(
entity = SaleProducts::class,
parentColumn = "saleId",
entityColumn = "saleIdMap"
)
val saleProducts: List<SaleProducts>
)
Finally a function in the/an @Dao
annotated interface:-
@Transaction
@Query("SELECT * FROM Sale")
fun getAllSalesWithRespectiveListOfSalesProducts(): LiveData<List<SaleWithSaleProducts>>
For each Sale located by the query a SaleWithSaleProducts will be returned, each will have the List of of all the related child SaleProducts.
Demo
The following demonstrates the code above, noting the following additional functions in the AllDAOs interace (i.e. annotated with @Dao
) to allowing the insertion of Sale and SaleProducts rows.
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(sale: Sale): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(saleProducts: SaleProducts): Long
Also a pretty basic @Database
annotated class that allows use of the main thread for brevity of the demo (as such LiveData is not used/observed).
Finally some activity code as per :-
....
db= TheDatabase.getInstance(this)
dao=db.getAllDAOs()
val saleOnDate1Id = dao.insert(Sale(saleDate = 1))
val saleOnDate2Id = dao.insert(Sale(saleDate = 2))
val saleOnDate3Id = dao.insert(Sale(saleDate = 3))
dao.insert(SaleProducts(saleIdMap = saleOnDate1Id))
dao.insert(SaleProducts(saleIdMap = saleOnDate1Id))
dao.insert(SaleProducts(saleIdMap = saleOnDate1Id))
dao.insert(SaleProducts(saleIdMap = saleOnDate2Id))
dao.insert(SaleProducts(saleIdMap = saleOnDate2Id))
for (swsp in dao.getAllSalesWithRespectiveListOfSalesProducts()) {
val sb = StringBuilder()
for (sp in swsp.saleProducts) {
sb.append("\n\tID of SaleProducts is ${sp.saleProductId} it maps to (is a child of) the Sale with an ID of ${sp.saleIdMap}")
}
Log.d("DBINFO","Sale ID is ${swsp.sale.saleId} Sale Date is ${swsp.sale.saleDate}. It has ${swsp.saleProducts.size} child SaleProducts. they are:- $sb")
}
So 3 Sale rows are inserted with the saleId being stored in the respective saleOnDate?Id field.
Then 5 SaleProducts are inserted, the first 3 with the first Sale as the parent, the remaining 2 having the second Sale as the parent. The third Sale has no children.
The function to get the Sales with the SaleProducts is invoked. The resultant SaleWithSaleProducts is traversed writing the results to the Log.
The result being:-
D/DBINFO: Sale ID is 1 Sale Date is 1. It has 3 child SaleProducts. they are:-
ID of SaleProducts is 1 it maps to (is a child of) the Sale with an ID of 1
ID of SaleProducts is 2 it maps to (is a child of) the Sale with an ID of 1
ID of SaleProducts is 3 it maps to (is a child of) the Sale with an ID of 1
D/DBINFO: Sale ID is 2 Sale Date is 2. It has 2 child SaleProducts. they are:-
ID of SaleProducts is 4 it maps to (is a child of) the Sale with an ID of 2
ID of SaleProducts is 5 it maps to (is a child of) the Sale with an ID of 2
D/DBINFO: Sale ID is 3 Sale Date is 3. It has 0 child SaleProducts. they are:-