微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

查询一对多关系Android Room

如何解决查询一对多关系Android Room

我有一对多的关系。一个 Parent 可以容纳多个 Child 实体。 子级拥有 date 类型的属性 Instant

我创建了一个结合了这两个实体的类:

data class Combined(
    @Embedded val parent: Parent,@Relation(
        parentColumn = "elementId",entityColumn = "Id"
    )
    val children: List<Child>
)

我知道我可以像这样检索所有 Combined 元素:

@Transaction
@Query("SELECT * FROM Parent")
fun getCombined(): Flow<List<Combined>>

有什么方法可以检索仅包含特定日期范围内的孩子的 List<Combined>

解决方法

有什么方法可以检索只包含特定日期范围内的孩子的列表?

没那么容易。 @Relation 的工作原理是根据第二个查询获取父级的所有子级(因此推荐使用 @Transaction)。过滤仅适用于父级的选择。

问题在于,如果您使用类似 :-

SELECT * FROM parent JOIN child ON child.Id = parent.elementId WHERE date BETWEEN fromDate and toDate;

返回的是一个列表,其中每一行都包含父项和单个子项。要提取父级和所有过滤后的子级,需要对列表进行处理以将父级与其子级分开。

基于可用代码,这里有一个示例。

  • 一个例外是 Id 列,它通常是子项的 ID,因此添加了一个 parentId 列来保存父项的 ID。

  • 另一个例外是,为了方便/简洁,日期只是一个字符串 (TEXT),而不是需要 @TypeConverter 的 Instant。

  • 第三个例外是使用了伴随对象(将在适当的时候显示)。

首先新建一个POJO,Filter用于提取过滤后的列表(父母和孩子各占一行)

data class Filter(
    @Embedded
    val parent: Parent,@Embedded
    val child: Child
)

接下来是一个适合查询的 Dao getFiltered(与上面的差不多):-

@Query("SELECT * FROM parent JOIN child ON child.parentId = parent.elementId WHERE date BETWEEN :fromDate AND :toDate")
    abstract fun getFiltered(fromDate: String,toDate:String): List<Filter>
  • 请注意,这将返回 Filter 的列表,而不是 组合
  • 的列表

最后一个带有伴随对象的 Child 以便于从 Filters 列表中构建 Combined 列表:-

@Entity
data class Child(
    @PrimaryKey
    var id: Long? = null,var parentId: Long,@NotNull
    var childName: String,var date: String
) {

    companion object {

        fun buildParentsWithChildren(filter: List<Filter>): List<Combined> {
            var rv: ArrayList<Combined> = arrayListOf()
            if (filter.size < 1) return rv
            for (f: Filter in filter) {
                addChild(rv,getOrAddParent(rv,f),f)
            }
            return rv
        }

        private fun getOrAddParent(built: ArrayList<Combined>,f: Filter): Int {
            for (i in 0..(built.size-1)) {
                if (built.get(i).parent.parentName == f.parent.parentName) {
                    return i
                }
            }
            var newCombined: Combined = Combined(f.parent,emptyList())
            built.add(newCombined)
            return built.size - 1
        }

        private fun addChild(built: ArrayList<Combined>,parentIx: Int,f: Filter) {
            var currentChildren: ArrayList<Child> = arrayListOf<Child>()
            currentChildren.addAll(built.get(parentIx).children)
            currentChildren.add(f.child)
            built[parentIx] = Combined(parent = built.get(parentIx).parent,currentChildren)
        }
    }
}

示例

这是使用上述内容的示例。

首先它建立一些数据,3个父母5个孩子(第一个父母3个,第二个父母2个,第三个父母0个):-

enter image description here

enter image description here

然后它使用查询来提取一些数据并将其转换为列表。然后遍历列表输出到日志。

这是一个活动的代码:-

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

    var p1 = dao.insert(Parent(parentName = "Parent1"))
    var p2 = dao.insert(Parent(parentName = "Parent2"))
    var p3 = dao.insert(Parent(parentName = "Parent3"))

    dao.insert(Child(parentId = p1,childName = "Child1",date = "2000-01-01"))
    dao.insert(Child(parentId = p1,childName = "Child2",date = "2003-01-01"))
    dao.insert(Child(parentId = p1,childName = "Child3",date = "2005-01-01"))
    dao.insert(Child(parentId = p2,childName = "Child4",date = "2006-01-01"))
    dao.insert(Child(parentId = p2,childName = "Child5",date = "2007-01-01"))


    for(cmbnd: Combined in Child.buildParentsWithChildren(dao.getFiltered("2004-12-31","2010-01-01"))) {
        Log.d("DBINFO","Parent is ${cmbnd.parent.parentName}")
        for(c: Child in cmbnd.children)
            Log.d("DBINFO","Child is ${c.childName} date is ${c.date}")
    }

结果 :-

2021-08-02 08:38:50.426 D/DBINFO: Parent is Parent1
2021-08-02 08:38:50.426 D/DBINFO: Child is Child3 date is 2005-01-01
2021-08-02 08:38:50.426 D/DBINFO: Parent is Parent2
2021-08-02 08:38:50.427 D/DBINFO: Child is Child4 date is 2006-01-01
2021-08-02 08:38:50.427 D/DBINFO: Child is Child5 date is 2007-01-01

即第一个父母的 3 个孩子中只有 1 个的日期在 2005 年和 2009 年之间。但是,第二个父母的两个孩子都符合日期范围。第三个父母什么都没有。

,

附加

关于评论:-

我实际上更喜欢使用空列表获取父级 3,而不是不返回它。

这实际上在数据库方面相对困难/复杂,因为您要求考虑“非关系”(因为想要一个更好的术语)。

但是,使用不同的方法,即获取所有父级,然后让每个父级获取过滤后的子级(如果适用,则无)。

因此修改上述内容以包括:-

在@Dao AllDao

  1. 添加查询以将所有父母(您可能有这个)作为列表。
  2. 添加查询以获取过滤后的子项作为列表

例如:-

@Query("SELECT * FROM parent")
abstract fun getAllParents(): List<Parent>

@Query("SELECT * FROM child WHERE parentId = :parentId AND date BETWEEN :fromDate AND :toDate")
abstract  fun getFilteredChildrenForAParent(parentId: Long,fromDate: String,toDate: String): List<Child>

如果@Dao AllDao 是一个抽象类,而不是一个接口。 3. 添加一个提取所有父级的函数,通过它们循环获取过滤后的子级,例如:-

fun getAllParentsWithFilteredChildren(fromDate: String,toDate: String): List<Combined> {
    var rv: ArrayList<Combined> = arrayListOf()
    for(p: Parent in this.getAllParents()) {
        rv.add(Combined(parent = p,this.getFilteredChildrenForAParent(p.elementId!!,fromDate,toDate)))
    }
    return rv
}

否则(如果您不希望@Dao 成为抽象类并因此成为接口)在其他地方(在 Child 数据类中)包含一个函数,例如:-

    fun getAllParentsWithFilteredChildren(dao: AllDao,toDate: String): List<Combined> {
        var rv: ArrayList<Combined> = arrayListOf()
        for (p: Parent in dao.getAllParents()) {
            rv.add(Combined(p,dao.getFilteredChildrenForAParent(p.elementId!!,toDate)))
        }
        return rv
    }
  • 注意细微的差别,dao 被传递给函数

结果

修改第一个示例中的活动代码以包括:-

    for(cmbnd: Combined in dao.getAllParentsWithFilteredChildren("2004-12-31","2010-01-01")) {
        Log.d("DBINFOV2","Parent is ${cmbnd.parent.parentName}")
        for(c: Child in cmbnd.children)
            Log.d("DBINFOV2","Child is ${c.childName} date is ${c.date}")
    }

    for (cmbnd: Combined in Child.getAllParentsWithFilteredChildren(dao,"2004-12-31","2010-01-01")) {
        Log.d("DBINFOV3","Parent is ${cmbnd.parent.parentName}")
        for(c: Child in cmbnd.children)
            Log.d("DBINFOV3","Child is ${c.childName} date is ${c.date}")
    }

然后是结果(忽略第一个示例的结果)然后 logcat 包括:-

2021-08-03 08:33:30.812 D/DBINFOV2: Parent is Parent1
2021-08-03 08:33:30.812 D/DBINFOV2: Child is Child3 date is 2005-01-01
2021-08-03 08:33:30.812 D/DBINFOV2: Parent is Parent2
2021-08-03 08:33:30.812 D/DBINFOV2: Child is Child4 date is 2006-01-01
2021-08-03 08:33:30.812 D/DBINFOV2: Child is Child5 date is 2007-01-01
2021-08-03 08:33:30.812 D/DBINFOV2: Parent is Parent3


2021-08-03 08:33:30.817 D/DBINFOV3: Parent is Parent1
2021-08-03 08:33:30.817 D/DBINFOV3: Child is Child3 date is 2005-01-01
2021-08-03 08:33:30.817 D/DBINFOV3: Parent is Parent2
2021-08-03 08:33:30.817 D/DBINFOV3: Child is Child4 date is 2006-01-01
2021-08-03 08:33:30.817 D/DBINFOV3: Child is Child5 date is 2007-01-01
2021-08-03 08:33:30.817 D/DBINFOV3: Parent is Parent3
  • 即包括没有孩子的 Parent3

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。