如何解决查询一对多关系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个):-
和
然后它使用查询来提取一些数据并将其转换为列表。然后遍历列表输出到日志。
这是一个活动的代码:-
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
- 添加查询以将所有父母(您可能有这个)作为列表。
- 添加查询以获取过滤后的子项作为列表
例如:-
@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 举报,一经查实,本站将立刻删除。