简体   繁体   English

如何观察嵌套关系pojo android房间的livedata

[英]How to observe livedata of nested relation pojo android room

I have the following schema in my application (simplified for ease)我的应用程序中有以下架构(为方便起见进行了简化)

@PrimaryKey(serverId)
data class Server(
   val serverId: Long,
   val serverDescription: String
)

@PrimaryKey(siteServerId, siteSiteId)
data class Site(
   val siteServerId: Long,
   val siteSiteId: Int,
   val siteDescription: String
)

@PrimaryKey(groupServerId, groupGroupId)
data class Group(
   val groupServerId: Long,
   val groupSiteId: Int,
   val groupGroupId: Int,
   val groupDescription: String
)

@PrimaryKey(messageServerId, messageId)
data class Message(
   val messageServerId: Long,
   val messageId: String,
   val messageGroupId: Int,
   val messageBody: String,
   val messageReadStatus: Boolean
)

I want to show in the UI a list as the following example我想在 UI 中显示一个列表,如下例所示

Server Description 1 
   Site 1 -> UnReadMessages 8 
      Group 1 -> UnReadMessages 5 
      Group 2 -> UnReadMessages 3 
   Site 2 -> UnReadMessages 5 
      Group 3 -> unReadMessages 1 
      Group 4 -> unReadMessages 4 

Server Description 2 
   Site 1 -> UnReadMessages 4
      Group 1 -> UnReadMessages 1 
      Group 5 -> UnReadMessages 3 

The Servers are the different Accounts that i have.服务器是我拥有的不同帐户。 There is a possibility that i could be in the same (SiteID, GroupID) from 2 Servers.我有可能在 2 个服务器的同一个(SiteID、GroupID)中。

In order to achieve this i have the following pojos为了实现这一点,我有以下 pojos

data class SiteItem(
    @Embedded
    val site: Site,

    @Relation(entity = Group::class, entityColumn = "groups_site_id", parentColumn = "sites_site_id")
    val groupItems: List<GroupItem>
) {
    fun getSiteUnreadCount(): Int =
        groupItems.sumOf {
            it.getGroupUnreadCount()
        }
}

data class GroupItem(
    @Embedded
    var group: Group,

    @Relation(entity = Message::class, parentColumn = "groups_group_id", entityColumn = "messages_group_id")
    var messages: List<Message>
) {
    fun getGroupUnreadCount(): Int {
        return messages.filter { it.isIncome == 1 && it.isRead == false }
    }
}

So using the Room 2.4.2 i have the following query所以使用 Room 2.4.2 我有以下查询

@Query("""
        Select * from servers
        inner join sites on servers.server_id = sites.sites_server_id"""
    )
    fun getServerItems(): LiveData<Map<Server, List<SiteItem>>>

The expected result should have been something like that预期的结果应该是这样的

RESULT = Map (
   key: Server(serverId: 1, serverDescription: "Server 1"),
   value: [
      SiteItem(
         site: Site(siteServerId: 1, siteSiteId: 1, siteDescr: "Site 1"),
         groupItems: [
         GroupItem(
             Group(groupServerId: 1, groupSiteId: 1, groupGroupId: 1, groupDesc: "Group 1"), List<Message> : [...messages...],
         GroupItem(
             Group(groupServerId: 1, groupSiteId: 1, groupGroupId: 2, groupDesc: "Group 2"), List<Message> : [...messages...]
      )],
      SiteItem(
         site: Site(siteServerId: 1, siteSiteId: 2, siteDescr: "Site 2"),
         groupItems: [
         GroupItem(
             Group(groupServerId: 1, groupSiteId: 2, groupGroupId: 3, groupDesc: "Group 3"), List<Message> : [...messages...],
         GroupItem(
             Group(groupServerId: 1, groupSiteId: 2, groupGroupId: 4, groupDesc: "Group 4"), List<Message> : [...messages...]
      )]
   ],


   key: Server(serverId: 2, serverDescription: "Server 2"),
   value: [
      SiteItem(
         site: Site(siteServerId: 2, siteSiteId: 1, siteDescr: "Site 1"),
         groupItems: [
         GroupItem(
             Group(groupServerId: 2, groupSiteId: 1, groupGroupId: 1, groupDesc: "Group 1"), List<Message> : [...messages...],
         GroupItem(
             Group(groupServerId: 2, groupSiteId: 1, groupGroupId: 5, groupDesc: "Group 5"), List<Message> : [...messages...]
      )]
   ]

However what i get can be shown in the following image但是我得到的可以显示在下图中除了 Server 键和 SiteItem.sites,所有组都列在 GroupItems 列表中,尽管不属于 key.ServerId

Does anyone knows how can i solve that?有谁知道我该如何解决?

Original Question, to which this answer applies (as the question has been drastically changed):-原始问题,此答案适用(因为问题已发生巨大变化):-


How to observe LiveData of nested relation POJO android room The schema of my Database has 4 tables Server, Sites, Groups and Messages.如何观察嵌套关系 POJO android room 的 LiveData 我的数据库的架构有 4 个表服务器、站点、组和消息。 Every Server has many Sites and every Site has many Groups.每个服务器都有许多站点,每个站点都有许多组。

There is a POJO called GroupItem that holds the Group and the unreadMessages of the Group.有一个名为 GroupItem 的 POJO,它保存了 Group 和 Group 的 unreadMessages。

data class GroupItem(
    @Embedded
    val group: Group,
    
    @ColumnInfo(name = "unreadCounter")
    val unreadCounter: Int
)

The unReadCounter field can be found only with a join with the Messages table. unReadCounter 字段只能在与 Messages 表的连接中找到。

I also have a POJO that holds a Site with its GroupItems我还有一个 POJO,它拥有一个带有 GroupItems 的站点

data class SiteItem(
    @Embedded
    val site: Site,

    @Relation(entity = Group::class, entityColumn = "groups_site_id", parentColumn = "sites_site_id")
    val groupItem: List<GroupItem>
)

So in my UI i want to display a list of a Server with all its SiteItems.因此,在我的 UI 中,我想显示一个服务器列表及其所有 SiteItems。 So i thought of using Room 2.4.2 by observing the query所以我想通过观察查询来使用Room 2.4.2

@Query("""
        Select * from sites
        inner join servers on servers.server_id = sites.sites_server_id"""
    )
    fun getServerItems(): LiveData<Map<Server, List<SiteItem>>>

When i try to build the project i get the error当我尝试构建项目时出现错误

There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: unreadCounter)查询有问题:[SQLITE_ERROR] SQL 错误或缺少数据库(没有这样的列:unreadCounter)

private final java.util.List<com.example.model.entities.pojos.GroupItem> groupItem = null

Answer Provided提供答案

I can understand the issue and why this is happening.我可以理解这个问题以及为什么会这样。 Its because it cannot know how to fill up this field.这是因为它不知道如何填写该字段。 I guess i have to do a double query or something in a transaction, but how can i do that since i want to return a LiveData of a Map to the UI?我想我必须在事务中进行双重查询或其他操作,但我怎么能这样做,因为我想将 Map 的 LiveData 返回到 UI? How can i solve this kind of a problem?我该如何解决这种问题?

As you have:-正如你所拥有的: -

@ColumnInfo(name = "unreadCounter")
val unreadCounter: Int

a) the @Columninfo annotation has no purpose so really you just have val unreadCounter: Int b) there will be no such column, so it would need to be generated. a) @Columninfo 注释没有任何用途,所以实际上你只有val unreadCounter: Int b) 不会有这样的列,所以需要生成它。

  • eg something along the lines of SELECT *,(SELECT count(read_status) FROM messages WHERE groups_group_id = g.group_id AND read_status) AS unreadCounter FROM groups AS g WHERE group_id=:group_id .例如SELECT *,(SELECT count(read_status) FROM messages WHERE groups_group_id = g.group_id AND read_status) AS unreadCounter FROM groups AS g WHERE group_id=:group_id
  • However, to incorporate this would be relatively complex.但是,将其合并起来会相对复杂。
  • Note the column read_status has been made up and will probably not reflect what you have.请注意,列 read_status 已组成,可能不会反映您拥有的内容。

Assuming that, in addition to your description of the relationships, a Group has many messages which can be either read or unread the the following is an working example that gets the unread messages for a group and also for a site.假设除了您对关系的描述之外,一个组还有许多可以阅读或未读的消息,以下是一个工作示例,它获取组和站点的未读消息。

It uses a function rather than a query to ascertain the unread count.它使用函数而不是查询来确定未读计数。

First the @Entity annotated classes Server , Site , Group and Message for the 4 tables :-首先为 4 个表添加 @Entity 注释类ServerSiteGroupMessage :-

@Entity(tableName = "servers")
data class Server(
    @PrimaryKey
    var server_id: Long?=null,
    var server_name: String
)


@Entity(tableName ="sites")
data class Site(
    @PrimaryKey
    var site_id: Long?=null,
    var sites_server_id: Long,
    var site_name: String
)


@Entity(tableName = "groups")
data class Group(
    @PrimaryKey
    var group_id: Long?=null,
    var groups_site_id: Long,
    var group_name: String
)


@Entity(tableName = "messages")
data class Message(
    @PrimaryKey
    var message_id: Long?=null,
    var groups_group_id: Long,
    var message_name: String,
    var read_status: Boolean = false
)
  • Obviously these will probably not reflect you actual classes.显然,这些可能不会反映您的实际课程。

Now some POJO classes :-现在一些 POJO 类:-

First GroupWithMessages :-第一个GroupWithMessages :-

data class GroupWithMessages(
    @Embedded
    var group: Group,
    @Relation(entity = Message::class, parentColumn = "group_id", entityColumn = "groups_group_id")
    var messages: List<Message>
) {

    fun getUnreadCounter(): Int {
        var rv: Int = 0
        for(m in messages) {
            if (!m.read_status) {
                rv++
            }
        }
        return rv
    }
}
  • with a function to retrieve the unread messages.具有检索未读消息的功能。

and second an adaptation of your SiteItem POJO :-其次是对您的SiteItem POJO 的改编:-

data class SiteItem(
    @Embedded
    var site: Site,
    @Relation(entity = Group::class, parentColumn = "site_id", entityColumn = "groups_site_id")
    var groupList: List<GroupWithMessages>

) {
    fun getSiteUnreadCount(): Int {
        var rv: Int = 0
        for (g in groupList) {
            rv = rv + g.getUnreadCounter()
        }
        return rv
    }
}
  • with an additional function that will retrieve the unread message count for all groups in the Site.具有一个附加功能,该功能将检索站点中所有组的未读消息计数。

All of the dao functions in interface Alldao :- Alldao接口中的所有dao函数:-

@Dao
interface AllDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(server: Server): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(site: Site): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(group: Group): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(message: Message): Long
    
    @Transaction
    @Query("SELECT * FROM sites INNER JOIN servers on servers.server_id = sites.sites_server_id")
    fun getServerItems(): Map<Server, List<SiteItem>>
    
}
  • Note that for convenience and brevity LiveData has not been used请注意,为了方便和简洁,未使用 LiveData

an @Database annotated class TheDatabase :- @Database注释类TheDatabase :-

@Database(entities = [Server::class,Site::class,Group::class,Message::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDao(): AllDao

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance==null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

Finally putting it all together in an activity (designed to run just the once) which inserts various data and then uses the getServerItems function to drive a loop that reports on the data:-最后将它们放在一个活动中(设计为只运行一次),该活动插入各种数据,然后使用getServerItems函数驱动一个报告数据的循环:-

const val TAG = "DBINFO"
class MainActivity : AppCompatActivity() {

    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()

        val s1id = dao.insert(Server(server_name = "Server1"))
        val s2id = dao.insert(Server(server_name = "Server2"))

        val site1id = dao.insert(Site(sites_server_id = s1id, site_name = "Site1"))
        val site2Id = dao.insert(Site(sites_server_id = s1id, site_name = "Site2"))
        val site3Id = dao.insert(Site(sites_server_id = s2id, site_name = "Site3"))
        val site4Id = dao.insert(Site(sites_server_id = s2id, site_name = "Site4"))

        val g1id = dao.insert(Group(groups_site_id = site1id, group_name = "Group1"))
        val g2id = dao.insert(Group(groups_site_id = site1id, group_name = "Group2"))
        val g3id = dao.insert(Group(groups_site_id = site1id, group_name = "Group3"))

        val g4Id = dao.insert(Group(groups_site_id = site2Id, group_name = "Group4"))

        dao.insert(Message(groups_group_id = g1id, message_name = "M1"))
        dao.insert(Message(groups_group_id = g1id, message_name = "M2", read_status = true))
        dao.insert(Message(groups_group_id = g1id, message_name = "M3"))
        dao.insert(Message(groups_group_id = g1id, message_name = "M4"))

        for(si  in dao.getServerItems()) {
            Log.d(TAG,"Server is ${si.key.server_name} ID is ${si.key.server_id} it has ${si.value.size} sites.")
            for (s in si.value) {
                Log.d(TAG,"\tSite is ${s.site.site_name} there are ${s.groupList.size} Groups with ${s.getSiteUnreadCount()} unread messages.")
                for(g in s.groupList) {
                    Log.d(TAG,"\t\tGroup is ${g.group.group_name} there are ${g.messages.size} messages, ${g.getUnreadCounter()} unread")
                }
            }
        }
    }
}

Running the above results in the log :-在日志中运行上述结果:-

D/DBINFO: Server is Server1 ID is 1 it has 2 sites.
D/DBINFO:   Site is Site1 there are 3 Groups with 3 unread messages.
D/DBINFO:       Group is Group1 there are 4 messages, 3 unread
D/DBINFO:       Group is Group2 there are 0 messages, 0 unread
D/DBINFO:       Group is Group3 there are 0 messages, 0 unread
D/DBINFO:   Site is Site2 there are 1 Groups with 0 unread messages.
D/DBINFO:       Group is Group4 there are 0 messages, 0 unread
D/DBINFO: Server is Server2 ID is 2 it has 2 sites.
D/DBINFO:   Site is Site3 there are 0 Groups with 0 unread messages.
D/DBINFO:   Site is Site4 there are 0 Groups with 0 unread messages.

ie of the 4 messages added (to Group1, which has Site1 as it's parent which has Server1 as it's parent) 3 are unread an 1 is read.即添加的 4 条消息中(到 Group1,它的父节点为 Site1,父节点为 Server1),其中 3 条未读,1 条已读。

Aditional额外的

Replicating you original (aka demonstrating the "complexity") here's some additional code.复制您的原始代码(也就是展示“复杂性”)这里有一些额外的代码。

GroupItem and another version of of SiteItem ie SiteItemV2 GroupItemSiteItem的另一个版本,即SiteItemV2

/* Additional */
data class SiteItemV2(
    @Embedded
    var site: Site,
    @Relation(entity = Group::class, parentColumn = "site_id", entityColumn = "groups_site_id")
    var groupList: List<GroupItem>,
    var unreadCounter: Int /* freebie */
)
data class GroupItem(
    @Embedded
    var group: Group,
    var unreadCounter: Int
)

The associated @Dao functions :-相关的@Dao 函数:-

/* get the group and group's unread message count */
@Query("SELECT *,(SELECT count(read_status) FROM messages WHERE messages.groups_group_id = groups.group_id AND NOT read_status) AS unreadCounter FROM groups WHERE groups_site_id=:siteid;")
fun getGroupWithReadCount(siteid: Long): List<GroupItem>


/* combine getServerItems and the getGroupWithReadCount to output Map<Server, List<SiteItemV2> */
@Transaction
@Query("")
fun getServerItemsV3(): Map<Server, List<SiteItemV2>> {
    var rv = mutableMapOf<Server,List<SiteItemV2>>()
    var currentGroupItemList = ArrayList<GroupItem>()
    var currentSiteItemList = ArrayList<SiteItemV2>()
    for(b in getServerItems()) {
        var currentUnreadCount = 0 /* Site level unread counter */
        for(si in b.value) {
            for (gi in getGroupWithReadCount(si.site.site_id!!)) {
                currentGroupItemList.add(GroupItem(gi.group,gi.unreadCounter))
                currentUnreadCount +=gi.unreadCounter
            }
            currentSiteItemList.add(SiteItemV2(si.site,currentGroupItemList.toList(),currentUnreadCount))
            currentUnreadCount = 0
            currentGroupItemList.clear()
        }
        rv[b.key] = currentSiteItemList.toList()
        currentSiteItemList.clear()
    }
    return rv
}

and the activity code that uses the above:-以及使用上述内容的活动代码:-

    /* Additional */
    for(si in dao.getServerItemsV3()) {
        Log.d(TAG+"EX03","Server is ${si.key.server_name} ID is ${si.key.server_id} is has ${si.value.size} sites.")
        for (s in si.value) {
            Log.d(TAG+"EX03","\tSite is ${s.site.site_name} ID is ${s.site.site_id}. The are ${s.unreadCounter} unread messages for the site accross ${s.groupList.size} groups.")
            for (g in s.groupList) {
                Log.d(
                    TAG + "EX03",
                    "\t\tGroup is ${g.group.group_name} unread messages = ${g.unreadCounter}"
                )
            }
        }
    }

and the result :-结果:-

D/DBINFOEX03: Server is Server1 ID is 1 is has 2 sites.
D/DBINFOEX03:   Site is Site1 ID is 1. The are 3 unread messages for the site accross 3 groups.
D/DBINFOEX03:       Group is Group1 unread messages = 3
D/DBINFOEX03:       Group is Group2 unread messages = 0
D/DBINFOEX03:       Group is Group3 unread messages = 0
D/DBINFOEX03:   Site is Site2 ID is 2. The are 0 unread messages for the site accross 1 groups.
D/DBINFOEX03:       Group is Group4 unread messages = 0
D/DBINFOEX03: Server is Server2 ID is 2 is has 2 sites.
D/DBINFOEX03:   Site is Site3 ID is 3. The are 0 unread messages for the site accross 0 groups.
D/DBINFOEX03:   Site is Site4 ID is 4. The are 0 unread messages for the site accross 0 groups.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Room android LiveData 仅观察调用 2 次 - Room android LiveData observe call only 2 times 如何在房间数据库类中获取或观察实时数据 - how to get or observe livedata in room database class Android LiveData - 观察复杂/嵌套的对象 - Android LiveData - observe complex/nested objects 如何用Android Room表示嵌套的@Relation? - How to represent nested @Relation with Android Room? 如何根据 LiveData.observe 收到的信息更新 ROOM 数据库? - How to update ROOM Database on information received on LiveData.observe? 在Room中,如何在POJO中指定父实体关系? - In Room, how to specify a parent entity relation in a POJO? 如何观察 LiveData<pagedlist> 在 kotlin 中使用 android 分页</pagedlist> - How to observe LiveData<PagedList> with android paging in kotlin 如何在 Android 中以正确的方式通过多次观察来观察实时数据? - How to observe the livedata with multiple observe for correct way in Android? Android ViewModel LiveData 观察 - Android ViewModel LiveData observe Android ROOM - 如何观察 LiveData 变化(每次设置日历时)并将 LiveData 列表结果发送到我的适配器? - Android ROOM - How can I Observe LiveData changes (every time my calendar is set-up) and send the LiveData list results to my adapter?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM