Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define one-to-many relationship among Room entities for Chat model

I started using Room database and went through several docs to create room entities. These are my relations. A Chat Channel can have Many Conversations. So this goes as one-to-many relationship. Hence i created entities as below.

Channel Entity

@Entity(primaryKeys = ["channelId"])
@TypeConverters(TypeConverters::class)
data class Channel(
    @field:SerializedName("channelId")
    val channelId: String,
    @field:SerializedName("channelName")
    val channelName: String,
    @field:SerializedName("createdBy")
    val creationTs: String,
    @field:SerializedName("creationTs")
    val createdBy: String,
    @field:SerializedName("members")
    val members: List<String>,
    @field:SerializedName("favMembers")
    val favMembers: List<String>
) {
  // Does not show up in the response but set in post processing.
  var isOneToOneChat: Boolean = false
  var isChatBot: Boolean = false
}

Conversation Entity

@Entity(primaryKeys = ["msgId"],
    foreignKeys = [
        ForeignKey(entity = Channel::class,
                parentColumns = arrayOf("channelId"),
                childColumns = arrayOf("msgId"),
                onUpdate = CASCADE,
                onDelete = CASCADE
        )
    ])
@TypeConverters(TypeConverters::class)
data class Conversation(

    @field:SerializedName("msgId")
    val msgId: String,
    @field:SerializedName("employeeID")
    val employeeID: String,
    @field:SerializedName("channelId")
    val channelId: String,
    @field:SerializedName("channelName")
    val channelName: String,
    @field:SerializedName("sender")
    val sender: String,
    @field:SerializedName("sentAt")
    val sentAt: String,
    @field:SerializedName("senderName")
    val senderName: String,
    @field:SerializedName("status")
    val status: String,
    @field:SerializedName("msgType")
    val msgType: String,
    @field:SerializedName("type")
    val panicType: String?,
    @field:SerializedName("message")
    val message: List<Message>,
    @field:SerializedName("deliveredTo")
    val delivered: List<Delivered>?,
    @field:SerializedName("readBy")
    val read: List<Read>?

) {

data class Message(
        @field:SerializedName("txt")
        val txt: String,
        @field:SerializedName("lang")
        val lang: String,
        @field:SerializedName("trans")
        val trans: String
)

data class Delivered(
        @field:SerializedName("employeeID")
        val employeeID: String,
        @field:SerializedName("date")
        val date: String
)

data class Read(
        @field:SerializedName("employeeID")
        val employeeID: String,
        @field:SerializedName("date")
        val date: String
)

    // Does not show up in the response but set in post processing.
    var isHeaderView: Boolean = false
}

Now as you can see Conversation belongs to a Channel. When user sees a list of channels, i need to display several attributes of last Conversation in the list item. My question is, is it enough if i just declare relation like above or should i contain Converstion object in Channel class? What are the other ways in which i can handle it? Because UI needs to get most recent conversation that happened along with time, status etc. in each item of the channel list when user scrolls. So there should not be any lag in UI because of this when i query.

And how can i have recent Converstaion object in Channel object?

like image 803
androidbash Avatar asked Feb 15 '20 03:02

androidbash


Video Answer


1 Answers

I suggest create another class (not in DB, just for show in UI) like this:

data class LastConversationInChannel(
    val channelId: String,
    val channelName: String,
    val creationTs: String,
    val createdBy: String,
    val msgId: String,
    val employeeID: String,
    val sender: String,
    val sentAt: String,
    val senderName: String
    .
    .
    .
)

Get last Conversation in each Channel by this query:

 SELECT Channel.*
 ,IFNULL(LastConversation.msgId,'') msgId
 ,IFNULL(LastConversation.sender,'') sender
 ,IFNULL(LastConversation.employeeID,'') employeeID
 ,IFNULL(LastConversation.sentAt,'') sentAt
 ,IFNULL(LastConversation.senderName,'') senderName
 from Channel left join 
 (SELECT * from Conversation a  
 WHERE a.msgId IN ( SELECT b.msgId  FROM Conversation AS b 
                    WHERE a.channelId = b.channelId 
                    ORDER BY b.sentAt DESC  LIMIT 1 )) as LastConversation
 on Channel.channelId = LastConversation.channelId

then use it in your dao like this:

 @Query(" SELECT Channel.*\n" +
            " ,IFNULL(LastConversation.msgId,'') msgId\n" +
            " ,IFNULL(LastConversation.sender,'') sender\n" +
            " ,IFNULL(LastConversation.employeeID,'') employeeID\n" +
            " ,IFNULL(LastConversation.sentAt,'') sentAt\n" +
            " ,IFNULL(LastConversation.senderName,'') senderName\n" +
            " from Channel left join \n" +
            " (SELECT * from Conversation a  \n" +
            " WHERE a.msgId IN ( SELECT b.msgId  FROM Conversation AS b \n" +
            "                    WHERE a.channelId = b.channelId \n" +
            "                    ORDER BY b.sentAt DESC  LIMIT 1 )) as LastConversation\n" +
            " on Channel.channelId = LastConversation.channelId")
    fun getLastConversationInChannel(): LiveData<List<LastConversationInChannel>>

is it enough if i just declare relation like above or should i contain Converstion object in Channel class?

You should not contain Conversation in Channel class, because Room will create some columns for it in Conversation table.

like image 196
Iman Hamidi Avatar answered Sep 20 '22 20:09

Iman Hamidi