0

我有一对多的关系。一个人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>仅包含特定日期范围内的孩子的位置?

4

2 回答 2

1

有没有办法检索仅包含特定日期范围内的孩子的列表?

没那么容易。@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和伴随对象,以方便从过滤器列表中构建组合的列表:-

@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 个):-

在此处输入图像描述

在此处输入图像描述

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

这是来自 Activity 的代码:-

    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 年之间。但是,第二个父母的两个孩子都适合日期范围。第三个父母什么都没有。

于 2021-08-01T23:07:15.043 回答
1

额外的

关于评论: -

我实际上更愿意让 Parent 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, fromDate: String, toDate: String): List<Combined> {
        var rv: ArrayList<Combined> = arrayListOf()
        for (p: Parent in dao.getAllParents()) {
            rv.add(Combined(p,dao.getFilteredChildrenForAParent(p.elementId!!,fromDate,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
于 2021-08-02T22:47:10.373 回答