这对我来说非常棘手,因为我不知道是否可以通过SQLite查询或递归调用某个函数来完成.它变得复杂了,因为我也用FlowLiveData搭配ViewModel

基本上,我的实体是这样的

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Item::class,
            parentColumns = [ "id" ],
            childColumns = [ "parentId" ],
            onDelete = ForeignKey.CASCADE
        ),
    ],
    indices = [
        Index(value = [ "parentId" ]),
        Index(value = [ "sectionId" ])
    ],
)
class Item(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    var parentId: Long? = null,
    val name: String
)

现在,假设我有5个项目,每个项目都是下一个项目的父母

Item (id = 1, parentId = null, name = "Root item")
Item (id = 2, parentId = 1, name = "Item 2")
Item (id = 3, parentId = 2, name = "Item 3")
Item (id = 4, parentId = 3, name = "Item 4")
Item (id = 5, parentId = 4, name = "Item 5")

所以,我想要的是得到Item 5和它的所有祖先,这是Item 4,因为它是它的父母,Item 3,因为它是Item 4的父母,依此类推到第一个(它没有父母,所以它是我们停下来的地方)

现在我的刀有点乱,我有点卡住了.你认为这是如何实现的?

@Dao
interface ItemDao {
    @Query("SELECT * FROM Item WHERE itemId = :itemId")
    fun getById(itemId: Long): Flow<Item>

    fun getAncestors(itemId: Long): Flow<List<Item>> = flow {
        val item = getById(itemId)

        item.collect { it ->
            if (it.parentId != null) {
                val parent = getAncestors(it.parentId!!)
                val items = combine(item, parent ) { i, p ->
                    val allItems: ArrayList<Item> = arrayListOf()
                    allItems.add(i)
                    allItems.add(p)

                    allItems
                }

                emitAll(items)
            } else {
                emitAll(item)
            }
        }
    }
}

这不起作用(我不完全明白),很可能是因为错误地使用了流.我需要一个替代方案或一点帮助来理解这一点并摆脱困境

推荐答案

如果我理解正确的话.

所以5得4,4得3....直到1没有父母.那么我相信以下几点会起作用:-

@Query("WITH cte1(id,parentId,name) AS (SELECT * FROM item WHERE id=:itemId UNION ALL SELECT parentId,(SELECT parentId FROM item WHERE item.id = cte1.parentId),(SELECT name FROM item WHERE item.id = cte1.parentId) FROM cte1 WHERE parentId IS NOT NULL  LIMIT 10 /*<<<<< just in case limit to 10 iterations */) SELECT * FROM cte1;")
fun getPredecessorsOfId(itemId): Flow<List<Item>>
  • 注:以上内容未在项目中编码,因此未经测试,因此可能包含一些错误.
  • 这样做的好处是,所有这些都是在单个查询/数据库访问中完成的(可能您的问题之一是,您正在递归地返回流)

作为从SQLiteAngular (即忽略房间)进行的上述示例,请考虑以下带有注释的演示:-

DROP TABLE IF EXISTS item;
/* Create the demo table */
CREATE TABLE IF NOT EXISTS item(id INTEGER PRIMARY KEY, parentId INTEGER, name TEXT);
INSERT INTO item VALUES (1,null,'Root Item'),(2,1,'item2'),(3,2,'item3'),(4,3, 'item4'),(5,4,'item5');

SELECT * FROM item; /* Output 1 the item table in full */


/* Uses a Common Table Expression which is recursive due to the UNION with itself */
/* and will loop forever unless ended.*/
/* Common Table Expression can be considered as a temporary table that exists only for the duration of the query */
/* In this case the end is when the parentId is null due to WHERE parentId IS NOT NULL */
/* However, as a precaution, LIMIT 10 will limit to 10 iterations */
/* Output 2 example 1 */
WITH cte1(id,parentId,name) AS (
    SELECT * FROM item WHERE id = 5 
    UNION ALL SELECT 
        parentId, /* the id is the parentId of the previous row */
        (SELECT parentId FROM item WHERE item.id = cte1.parentId), /* parentId is obtained from the item table using the parentId of the previous row */
        (SELECT name FROM item WHERE item.id = cte1.parentId) /* likewise for the name */
        FROM cte1 
        WHERE parentId IS NOT NULL  
        LIMIT 10 /*<<<<< just in case limit to 10 iterations */
    )
SELECT * FROM cte1;

/* Output 3 using an id mid-list */
WITH cte1(id,parentId,name) AS (
    SELECT * FROM item WHERE id = 3 
    UNION ALL SELECT 
        parentId,
        (SELECT parentId FROM item WHERE item.id = cte1.parentId),
        (SELECT name FROM item WHERE item.id = cte1.parentId) 
        FROM cte1 
        WHERE parentId IS NOT NULL  
        LIMIT 10
    )
SELECT * FROM cte1;

DROP TABLE IF EXISTS item; /* Cleanup demo environment */

结果(3项输出)如下:-

enter image description here

  • 包含数据的项目表

enter image description here

  • 第5项及其前身

enter image description here

  • 项目3及其前身

关于CTE以及WITH子句和递归的更深入解释,请参阅https://sqlite.org/lang_with.html.

Kotlin相关问答推荐

Scala性状线性化等价于Kotlin

为什么 <= 可以应用于 Int 和 Long,而 == 不能?

Kotlin 可空泛型

当 func 重载时,kotlin 如何确定调用哪个 func?

Kotlin 中私有集的完整语法 struct 是什么?

AIDL 中的 Parcelize 注释:Incompatible types: Object cannot be converted to MyCustomObject

为什么 Kotlin 扩展运算符在传递原始可变参数时需要 toTypedArray()?

如何为你的 Flutter 元素添加 Kotlin 支持?

.indices 在 kotlin 中的含义是什么?

Kotlin 和 Java 字符串拆分与正则表达式的区别

Gradle 同步失败:不支持的方法:KotlinPlatformContainer.supports()

Kotlin 枚举中的循环引用

Kotlin 创建snackbar

Kapt不适用于Android Studio 3.0中的AutoValue

如何将vararg转换为list?

在调用Kotlin数据类中的超类构造函数之前访问函数

重复构建失败To use Coroutine features, you must add `ktx`......

我们如何在Java注释声明中引用Kotlin常量?

lateinit 的 isInitialized 属性在伴随对象中不起作用

Kotlin for assertThat(foo, instanceOf(Bar.class))