使用 orderBy 从 Android 房间获取嵌入关系定义的 table

use orderBy to get embedded relation-defined table from Android Room

在 Android 房间查询此交易:

    @Transaction
    @Query("SELECT * FROM TPAGroup WHERE zuid=:zuid ORDER BY `index`")
    fun getGroupWithSecretsForZuid(zuid: String): List<TPAGroupWithSecrets>

这是我的数据 class :

data class TPAGroupWithSecrets(
        @Embedded val group: TPAGroup,
        @Relation(
                parentColumn = "groupId",
                entityColumn = "groupId"
        )
        var secrets: MutableList<TPASecrets>

)

我以正确的顺序获得了 TPAGroup,但尚未订购 TPASecrets!我怎样才能按正确的顺序排列它们,按它们的索引(这是两个表共有的列)排序?

当使用 @Relation 时,Room 获取相关对象,如您所见,没有任何特定顺序(顺序可能按主键排序,但这取决于 SQLite 的查询优化器)。

如果您需要订购它们,您可以

  1. 对返回的集合进行排序或
  2. 您可以有效地override/bypass Room 实现的@Relation 处理。
  3. 使用单个查询进行相应排序,然后从笛卡尔积构建结果(部分示例见底部)

这是 2 的工作示例

TPAGroup(编)

@Entity
data class TPAGroup(
    @PrimaryKey
    val groupId: Long? = null,
    val zuid: String,
    val index: Long,
)

TPASecrets(编造)

@Entity
data class TPASecrets(
    @PrimaryKey
    val secretId: Long? = null,
    val groupId: Long,
    val index: Long
)

TPAGroupWithSecrets(未更改)

data class TPAGroupWithSecrets(
    @Embedded val group: TPAGroup,
    @Relation(
        parentColumn = "groupId",
        entityColumn = "groupId"
    )
    var secrets: MutableList<TPASecrets>
)

@Dao 注释class

@Dao
interface AllDAO {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(tpaGroup: TPAGroup): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(tpaSecrets: TPASecrets): Long
    @Query("SELECT * FROM TPASecrets WHERE groupId=:groupId ORDER BY `index`;")
    fun getRelatedSecrets(groupId: Long): MutableList<TPASecrets>
    @Query("SELECT * FROM TPAGroup WHERE zuid=:zuid ORDER BY `index`;")
    fun getGroupsForZuid(zuid: String): MutableList<TPAGroup>

    @Transaction
    @Query("")
    fun getGroupWithSecretsForZuid(zuid: String): List<TPAGroupWithSecrets> {
        val rv = ArrayList<TPAGroupWithSecrets>()
        for(t in getGroupsForZuid(zuid)) {
            rv.add(TPAGroupWithSecrets(t,getRelatedSecrets(t.groupId!!)))
        }
        // rv.sortBy { .... }
        return rv
    }
}
  • 注意@Query,尤其是最后一个绕过 Rooms @Relation 处理的(即 TPAGroupWithSecrets 是在房间外构建的)

@Database 注释 class 将所有 Room 内容联系在一起 TheDatabase

@Database(entities = [TPAGroup::class,TPASecrets::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
        }
    }
}
  • 为了方便和简洁,在主线程上设置为 运行

终于在 Activity 中付诸行动了:-

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()

        dao.insert(TPAGroup(groupId = 1,zuid = "Group1", index = 10))
        dao.insert(TPAGroup(groupId = 2, zuid = "Group1", index = 9))
        dao.insert(TPAGroup(groupId = 3, zuid = "Group1", index = 11))

        dao.insert(TPASecrets(1000,1,5))
        dao.insert(TPASecrets(1010,groupId = 1, index = 4))
        dao.insert(TPASecrets(1020,1,3))
        dao.insert(TPASecrets(2000,2,5))
        dao.insert(TPASecrets(2010,2,6))
        dao.insert(TPASecrets(2020,2,7))
        dao.insert(TPASecrets(2030,2,1))
        dao.insert(TPASecrets(2040,2,2))
        dao.insert(TPASecrets(2050,2,3))
        dao.insert(TPASecrets(3000,3,1))
        dao.insert(TPASecrets(3010,3,0))

        for(tgws in dao.getGroupWithSecretsForZuid("Group1")) {
            Log.d("DBINFO","TPAGroup is ${tgws.group.groupId} Index is ${tgws.group.index}. It has ${tgws.secrets.size} Secrets, they are :-")
            for (s in tgws.secrets) {
                Log.d("DBINFO","\tSecret is ${s.secretId} Index is ${s.index}")
            }
        }
    }
}

输出到日志的结果(注意为了演示排序特意插入了数据):-

2022-04-13 21:37:29.220 D/DBINFO: TPAGroup is 2 Index is 9. It has 6 Secrets, they are :-
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2030 Index is 1
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2040 Index is 2
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2050 Index is 3
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2000 Index is 5
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2010 Index is 6
2022-04-13 21:37:29.220 D/DBINFO:   Secret is 2020 Index is 7
2022-04-13 21:37:29.221 D/DBINFO: TPAGroup is 1 Index is 10. It has 3 Secrets, they are :-
2022-04-13 21:37:29.221 D/DBINFO:   Secret is 1020 Index is 3
2022-04-13 21:37:29.221 D/DBINFO:   Secret is 1010 Index is 4
2022-04-13 21:37:29.221 D/DBINFO:   Secret is 1000 Index is 5
2022-04-13 21:37:29.221 D/DBINFO: TPAGroup is 3 Index is 11. It has 2 Secrets, they are :-
2022-04-13 21:37:29.221 D/DBINFO:   Secret is 3010 Index is 0
2022-04-13 21:37:29.221 D/DBINFO:   Secret is 3000 Index is 1
  • 所以 TPAGroups 根据索引值排序(2 索引 9 是第一个,3 索引 10 第二和 3 索引 11 第三)
  • 你可以很容易地看到 Secrets 是根据它们的索引而不是它们的主键 secrteId 排序的

选项 3 的部分示例

查询如

    SELECT * FROM TPAGroup JOIN TPASecrets ON TPASecrets.groupid = TPAGroup.groupid ORDER BY TPAGroup.`index` ASC, TPASecrets.`index`;

将产生数据(使用工作示例加载的数据):-

然后您需要有一个 POJO 来接收数据。但是,重复的列名 index 和 groupid 存在问题,因此查询更加复杂,需要别名 (AS),例如你可以使用

SELECT TPAGroup.*, TPASecrets.secretId, TPASecrets.`index` AS secretIndex FROM TPAGroup JOIN TPASecrets ON TPASecrets.groupid = TPAGroup.groupid ORDER BY TPAGroup.`index` ASC, TPASecrets.`index`;

因此从 TPASecrets 中删除重复的 groupid(在两者中始终具有相同的值)并且 TPASecrets 列是 aliased/renamed 作为 secretsIndex。显然 POJO 必须满足这一点。

然后您必须通过遍历结果来使用它的 TPASecrets 构建每个 TPAGroup。

不是 done/shown,因为大多数人倾向于选择选项 1 或 2,而倾向于选择选项 3。但是,选项 3 可能更有效,因为只有一个查询(不需要 @交易)。