Search code examples
androidandroid-roomandroid-architecture-components

Entity with lots of relationships with Android Room


I'm trying to update my Android knowledge with the new Android Architecture Components. I'm looking at how to persist data with Room, but it seems like it is practically unusable if you actually have relational data and I'm hoping you can help me understand something I've missed or confirm that Room really doesn't work for any kind of serious relational data.

Here's my problem in as small a nutshell as I can offer.

I've got an app for animal breeders and I have an Animal entity. Let's say I have a Weight entity which is a record of how much an animal weighed on a given date. An Animal has a to-many relationship to Weight to create a history of how much they weighed over time.

According to the Room training documentation, I would need to create a 3rd class called AnimalWithWeights like:

data class AnimalWithWeights(
    @Embedded val Animal: Animal,
    @Relation(
          parentColumn = "AnimalId",
          entityColumn = "AnimalId"
    )
    val Weights: List<Weight>
)

and add a function to my DAO like fun getAnimalsWithWeights(): List<AnimalWithWeights>.

@Transaction
@Query("SELECT * FROM Animal")
fun getAnimalsWithWeights(): List<AnimalWithWeights>

But I don't have just one relationship.

My Animal entity has relationships to other Animals like father, mother, surrogate mother, breeding parter, and relationships to other entity types like Breed, Breeder, Buyer, DNA, and to-many relationships for Weights, Notes, MedicalTreatments, MatingRecords, Ultrasounds, etc.

Unless I'm missing something, it sounds like I need to create a class that is AnimalWithWeights and then wrap that in a 4th class called AnimalWithWeightsAndNotes

and wrap that in a 5th class called AnimalWithWeightsAndNotesAndMedicalTreatments

and wrap that in a 6th class called AnimalWithWeightsAndNotesAndMedicalTreatmentsAndMatingRecords

and wrap that in a 7th class called AnimalWithWeightsAndNotesAndMedicalTreatmentsAndMatingRecordsAndUltrasounds...

and I haven't even gotten to ...AndFatherAndMotherAndSurrogateMotherAndBreedingParterAndBreedAndBreederAndBuyerAndDNA....

I'm currently using ORMLite to handle my sqlite persistence and it handles relational data well, but it seems like Room is only really designed to handle flat data.

Am I missing something with Room?

I'd like to be able to use some of the new features like LiveData<> so that I can get automatic callbacks when my data is updated and so that's one of the reasons I was looking into Room, but it just seems too rudimentary to be able to handle a realistic relational data model.


Solution

  • It's not clear from your post whether you're aware of possibility to link several @Relations in mentioned auxilary 3rd class. With Animal having one-to-many and one-to-one relations to other entities technically you need add just one final class AnimalWithAllStuff:

    data class AnimalWithAllStuff(
        @Embedded val Animal: Animal,
        @Relation(
              parentColumn = "AnimalId",
              entityColumn = "AnimalId"
        )
        val weights: List<Weight>,
        @Relation(
              parentColumn = "AnimalId",
              entityColumn = "AnimalId"
        )
        val notes: List<Notes>,
        ....
        @Relation(
              parentColumn = "fatherId",
              entityColumn = "AnimalId"
        )
        val father: Animal,
        @Relation(
              parentColumn = "motherId",
              entityColumn = "AnimalId"
        )
        val mother: Animal,
        ....
    )
    

    Technically on each @Relation Room internally invokes separate query and if you use LiveData Room sets tracker to notify observer on any change in Weight, Notes and so on.

    I doubt though that with that structure you can get, for example, father's and mother's weight, notes and so on.