How to join multiple instances of same object in Android Room?

感情迁移 提交于 2020-06-08 17:27:12

问题


NOTE: I cannot use relation as we had performance issue which is not reproduced on direct join query.

Until I added target user and from group user and corresponding

LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_target_user
LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_from_group_user

It worked well. But after addition, I can not figure out how to make those prefixes map in the query.

class ReadMessageEntity(
  @Embedded
  var message: MessageEntity,
  @Embedded
  var block: BlockEntity?,
  @Embedded
  var user: ChatUserRelationEntity,
  @Embedded(prefix = "target_user_")
  var targetUser: ChatUserEntity?,
  @Embedded(prefix = "from_group_user_")
  var fromGroupUser: ChatUserEntity?
)

This is which I'm trying to query:

  @Transaction
  @Query("""
    SELECT * FROM message_item 
    LEFT JOIN block_item ON block_item.block_message_id = message_item.message_local_id
    LEFT JOIN chat_user_relation ON chat_user_relation.chat_user_id = message_item.message_user_id
    LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_target_user
    LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_from_group_user
    WHERE message_item.message_chat_id = :chatId
    ORDER BY message_created_at ASC
    """)
  fun getMessagesByChat(chatId: String): Single<List<ReadMessageEntity>>

The error:

e: error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (ambiguous column name: main.chat_user.chat_user_id)


回答1:


In the query, you have to set an alias for the table chat_user as you are joining it twice in the same name which will confuse database engine so it tells you that the field is ambiguous.

EDIT:

I found something that may be similar to your issue: https://github.com/JetBrains/Exposed/issues/177

Also they referred to this code as an example to fix this issue:

    object Users : Table() {
        val id = varchar("id", 10).primaryKey()
        val name = varchar("name", length = 50)
        val residentialCityId = optReference("resid_city_id", Cities)
        val bornCityId = optReference("born_city_id", Cities)
    }

    object Cities : IntIdTable() {
        val name = varchar("name", 50) // Column
    }
    fun test() {
        val userTable1 = Users.alias("u1")
        val userTable2 = Users.alias("u2")

        Cities
            .innerJoin(userTable1, {Cities.id}, {userTable1[Users.residentialCityId]})
            .innerJoin(userTable2, {Cities.id}, {userTable2[Users.bornCityId]})
            .selectAll()
    }



回答2:


as mentioned Moayad .AlMoghrabi I should use table aliases. So I renamed LEFT JOIN chat_user to LEFT JOIN chat_user target_user. Note there is no AS between chat-user and target_user. (my mistake was that I tried to put it there). Behavior of prefixes is still a mystery to me as the prefix for chat_user works properly, but if I put the prefix to ChatUserRelationEntity then I receive unknown column error.

SELECT * FROM message_item 
LEFT JOIN block_item
    ON block_item.block_message_id = message_item.message_local_id
LEFT JOIN chat_user_relation 
    ON chat_user_relation.chat_user_relation_chat_user_id = message_item.message_user_id
    AND chat_user_relation.chat_user_relation_chat_user_chat_id = :chatId
LEFT JOIN chat_user target_user 
    ON target_user.chat_user_id = message_item.messages_target_user
LEFT JOIN chat_user from_group_user 
    ON from_group_user.chat_user_id = message_item.messages_from_group_user
WHERE message_item.message_chat_id = :chatId
ORDER BY message_created_at ASC



回答3:


Here is my join solution:

ChatsEntity

@Entity(tableName = "Chats",foreignKeys = [ForeignKey(entity = UserEntity::class,
parentColumns = ["id"], childColumns = ["userId"], onDelete = NO_ACTION),ForeignKey(entity = LastMessageEntity::class,
parentColumns = ["id"], childColumns = ["roomId"], onDelete = NO_ACTION)])
data class ChatsEntity(
  @PrimaryKey(autoGenerate = true)
  var id: Int? = null,
  @ColumnInfo(name = "roomId") var roomId: String,
  @ColumnInfo(name = "userId") var userId: String,
  @ColumnInfo(name = "count") var count: Int
)

LastMessageEntity

@Entity(tableName = "LastMessages")
data class LastMessageEntity(
   @PrimaryKey @ColumnInfo(name = "id") var id: String = "",
   @ColumnInfo(name = "message") var message: String = "",
   @ColumnInfo(name = "type") var type: String = ""
)

UserEntity

@Entity(tableName = "Users")
data class UserEntity(
   @PrimaryKey @ColumnInfo(name = "id") var id: String = "",
   @ColumnInfo(name = "username") var username: String = "",
   @ColumnInfo(name = "token") var token: String = ""
)

1. using relation

class ChatUserMessage {
@Embedded
var chat : ChatsEntity? = null

@Relation(parentColumn = "userId", entityColumn = "id")
var user : UserEntity? = null

@Relation(parentColumn = "roomId", entityColumn = "id")
var lastMessage : LastMessageEntity? = null

}

SQL Query

@Query("SELECT * FROM Chats")
fun getAllChats(): List<ChatUserMessage?>?

2. without using relation

class ChatUserMessage
{
  @Embedded
  var chat: ChatsEntity? = null

  @Embedded(prefix = "user_")
  var user:  UserEntity? = null

  @Embedded(prefix = "message_")
  var lastMessage: LastMessageEntity? = null
}

Query

@Query("SELECT Chats.*, LastMessages.id as  message_id,LastMessages.message as  message_message, LastMessages.type as  message_type, Users.id as user_id, Users.username as user_username, Users.token as user_token FROM Chats INNER JOIN LastMessages ON LastMessages.id = Chats.roomId INNER JOIN Users ON Users.id = Chats.userId")
fun getAllChats(): List<ChatUserMessage?>?


来源:https://stackoverflow.com/questions/61390192/how-to-join-multiple-instances-of-same-object-in-android-room

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!