Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to query a complex nested objects in Room?

I have several entities that you can see below, the problem is how to select nested lists properly when you have more than one level of nesting. If you take a look at Google repository on github, you can find very simple example, but not something complex. As documentation says here, Room doesn't allow object references. How to get ResultObject (and how it should look) including all nested lists?

ResultObject should have this info:

  1. List<Dialog>
  2. Dialog entity has List<Message>.
  3. Message has List<ImageContent>.
  4. ImageContent has List<ImageContentItem>

Dialog

@Entity(tableName = "dialog")
data class Dialog(@PrimaryKey val id String, val title: String)

Message

@Entity(tableName = "message",
        foreignKeys = [(ForeignKey(entity = Dialog::class, parentColumns ["id"], childColumns = ["dialogId"]))])
data class Message(@PrimaryKey val id: String, val dialogId: String)

ImageContent

@Entity(tableName = "image_content",
        foreignKeys = [(ForeignKey(entity = Message::class, parentColumns = ["id"], childColumns = ["messageId"]))])
data class ImageContent(@PrimaryKey val id: String, val messageId: String)

ImageContentItem

@Entity(tableName = "image_content_item",
        foreignKeys = [(ForeignKey(entity = ImageContent::class, parentColumns = ["id"], childColumns = ["imageContentId"]))])
data class ImageContentItem(val imageContentId: String,
                        @PrimaryKey(autoGenerate = true) val id: Long = 1)

DAO:

@Dao
interface DialogDao {
    @Query("SELECT * FROM dialog " +
            "INNER JOIN message ON message.dialogId = dialog.id " +
            "INNER JOIN image_content ON image_content.messageId = message.id " +
            "INNER JOIN image_content_item ON image_content_item.imageContentId = image_content.id")
    fun getDialogAllInformation(): Flowable<List<**ResultObject**>>
}
like image 900
Aleksandr Urzhumtcev Avatar asked Jan 09 '18 16:01

Aleksandr Urzhumtcev


People also ask

What does a room query return if nothing is found?

If nothing is found based on your criteria, it will return null.

What is DAO Room?

In Room, Data Access Objects or DAOs are used to access your application's persisted data. They are a better and modular way to access your database as compared to query builders or direct queries. A DAO can be either an interface or an abstract class.

Is Room database relational?

This article describes how to define the relationship between entities in the Room persistence library. Because SQLite is a relational database, entities can be related to one another.

What is DAO Android?

android.arch.persistence.room.Dao. Marks the class as a Data Access Object. Data Access Objects are the main classes where you define your database interactions. They can include a variety of query methods. The class marked with @Dao should either be an interface or an abstract class.


1 Answers

You can nest classes defining relationships as much as you want.

class DeepDialog {
    @Embedded lateinit var embedded: Dialog

    @Relation(parentColumn = "id", entityColumn = "dialogId", entity = Message::class)
    lateinit var messages: List<DeepMessage>
}

class DeepMessage {
    @Embedded lateinit var embedded: Message    

    @Relation(parentColumn = "id", entityColumn = "messageId", entity = ImageContent::class)
    lateinit var imageContents: List<DeepImageContent>
}

... etc

I think Room guys should come with something more elegant as this solution has at least two issues.

  1. It introduces quite annoying boilerplate.
  2. @Relation annotation can be used only on Lists or Sets so it doesn't cover scenarios where you have 1:1 relationship.

Imagine your Dialog would have only one Message by definition (instead of many). In that case, you wouldn't want your DeepDialog to have messages: List<> accessor but message: Message instead. The only way around I found is:

class DeepDialog {
    @Embedded lateinit var embedded: Dialog

    @Relation(parentColumn = "id", entityColumn = "dialogId", entity = Message::class)
    internal lateinit var messages: List<DeepMessage>
    val message get() = messages.firstOrNull()
}
like image 80
bakua Avatar answered Sep 22 '22 20:09

bakua