【问题标题】:How to query and filter relation entity in room database如何查询和过滤房间数据库中的关系实体
【发布时间】:2021-09-16 10:01:51
【问题描述】:

例如:

用户:

@Entity(tableName = "user")
data class UserEntity(
    @PrimaryKey
    @ColumnInfo(name = "id") val id: String,
    @ColumnInfo(name = "username") val username: String,
    @ColumnInfo(name = "name") val name: String,

发帖:

@Entity(
    tableName = "post",
    foreignKeys = [
        ForeignKey(
            entity = UserEntity::class,
            parentColumns = ["id"],
            childColumns = ["user_id"],
            onDelete = ForeignKey.CASCADE,
            onUpdate = ForeignKey.CASCADE
        )
    ],
    indices = [Index(value = ["user_id"])]
)
data class PostEntity(
    @PrimaryKey
    @ColumnInfo(name = "id") var id: String, 
    @ColumnInfo(name = "user_id") var userId: String,
    @ColumnInfo(name = "body") val body: String,
    @ColumnInfo(name = "like") val like: Int,
    @ColumnType(name = "type") val type: String,
)

数据

data class Data(
    @Embedded
    val user: UserEntity,
    @Relation(parentColumn = "id", entityColumn = "user_id")
    val post: List<PostEntity> = emptyList(),
)

如果我使用SELECT * FROM user,我得到了想要的数据(一个用户和所有帖子),但是我如何过滤特定类型的帖子,比如WHERE post.type = 'sth',这可能吗?

【问题讨论】:

    标签: sql android-room


    【解决方案1】:

    但是我如何过滤特定类型的帖子,

    这完全取决于您要过滤的内容。您可能需要与过滤器匹配但包含所有帖子(与类型无关)的数据对象,在这种情况下您可以使用:-

    @Transaction
    @Query("SELECT * FROM user JOIN post ON user.id = user_id WHERE post.type = :type")
    abstract fun getAllDataFiltered(type: String): List<Data>
    
    • 你会在哪里使用var mylist = yourdao.getAllDataFiltered("sth")之类的东西

    但是,由于 post 和 user 的 id 列都被命名为 id,因此歧义会受到干扰(用户 id 变成了 post id,因此没有底层的 post 对象提取)。

    如果您将 PostEntity 更改为:-

    @Entity(
        tableName = "post",
        foreignKeys = [
            ForeignKey(
                entity = UserEntity::class,
                parentColumns = ["id"],
                childColumns = ["user_id"],
                onDelete = ForeignKey.CASCADE,
                onUpdate = ForeignKey.CASCADE
            )
        ],
        indices = [Index(value = ["user_id"])]
    )
    data class PostEntity(
        @PrimaryKey
        @ColumnInfo(name = "postid") var id: String, //<<<<<<<<<< CHANGED
        @ColumnInfo(name = "user_id") var userId: String,
        @ColumnInfo(name = "body") val body: String,
        @ColumnInfo(name = "like") val like: Int,
        @ColumnInfo(name = "type") val type: String
    )
    

    然后消除歧义,返回的 Data 对象包括帖子类型为 sth 的各个用户的所有帖子。

    如果您只想返回只有过滤帖子的 Data 对象,那么您必须绕过 Room 的返回 FULL/COMPLETE 相关对象的技术。

    如果您将@Dao 类设为抽象类而不是接口,那么您将使用@Query,例如:-

    @Query("SELECT * FROM post WHERE user_id=:userid AND type=:type")
    abstract fun getPostsPerUserFiltered(userid: String, type: String): List<PostEntity>
    

    还有一个函数,例如:-

    fun getFullyFiltered(type: String): List<Data> {
        var rv: ArrayList<Data> = arrayListOf()
        for(d: Data in getAllDataFiltered(type)) {
            rv.add(Data(d.user,post = getPostsPerUserFiltered(d.user.id,type)))
        }
        return rv
    }
    

    这会过滤返回的 Data 对象,但随后会丢弃整个 PostEntity 列表(即每个帖子,无论过滤器如何),然后应用过滤后的帖子。

    如果您想要所有用户但只有匹配的帖子(因此可能是一个空的帖子列表),那么您可以使用以下功能:-

    fun getAllFilteredData(type: String): List<Data> {
        var rv: ArrayList<Data> = arrayListOf()
        for(u: UserEntity in getUsers()) {
            rv.add(Data(user = u, getPostsPerUserFiltered(u.id,type)))
        }
        return rv
    }
    

    即不会对用户应用过滤,而只会对帖子应用过滤。

    然后使用上面的内容(注意使用更改的列名(postid 而不是 id))然后考虑以下内容(使用了一个非常标准的 @Database 类):-

    class MainActivity : AppCompatActivity() {
        lateinit var db: TheDatabase
        lateinit var dao: AllDao
        val TAG: String = "DBINFO"
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            db = TheDatabase.getInstance(this)
            dao = db.getAllDao()
    
            dao.insert(UserEntity(id = "User1","User001","Mary"))
            dao.insert(UserEntity(id = "User2",username = "User002",name = "Sue"))
            dao.insert(UserEntity(id = "User3",username = "User003",name = "Tom"))
    
            dao.insert(PostEntity(id = "post1",userId = "User1",body = "post1 blah",type = "xxx",like = 0))
            dao.insert(PostEntity(id ="post2", userId = "User2",body ="post2 blah", type = "sth",like = 1))
            dao.insert(PostEntity(id = "post3", userId = "User1", body = "post3 blah", type = "sth", like = 3))
    
            /*
                No filtering applied
            */
            for(d: Data in dao.getAllData()) {
                logData(d,"ALL")
            }
            /*
                Return FULL Data objects (i.e. with ALL posts) but only those that
                have a post or posts that match the filter
            */
            for (d: Data in dao.getAllDataFiltered("sth")) {
                logData(d,"JOIN")
            }
            /*
                Return partial Data objects, but only for those that
                have a post that matches the type
             */
            for (d: Data in dao.getFullyFiltered("sth")) {
                logData(d,"FULL")
            }
    
            /*
                Return all partial Data Objects but with partial posts.
    
             */
            for (d: Data in dao.getAllFilteredData("sth")) {
                logData(d,"POST")
            }
        }
    
        private fun logData(data: Data,tagSuffix: String) {
            Log.d(TAG + tagSuffix,"Data for user ${data.user.id}, Name is ${data.user.name} etc")
            for (p: PostEntity in data.post) {
                Log.d(TAG + tagSuffix,"\t Post is ${p.id} Type is ${p.type} body is:-\n\t\t${p.body}")
            }
        }
    }
    

    输出到日志的结果是:-

    根本没有过滤:-

    2021-09-17 11:22:00.722 D/DBINFOALL: Data for user User1, Name is Mary etc
    2021-09-17 11:22:00.722 D/DBINFOALL:     Post is post1 Type is xxx body is:-
                post1 blah
    2021-09-17 11:22:00.723 D/DBINFOALL:     Post is post3 Type is sth body is:-
                post3 blah
    2021-09-17 11:22:00.723 D/DBINFOALL: Data for user User2, Name is Sue etc
    2021-09-17 11:22:00.723 D/DBINFOALL:     Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:22:00.723 D/DBINFOALL: Data for user User3, Name is Tom etc
    

    只有用户被过滤,因为 Room 获取用户的所有帖子 :-

    2021-09-17 11:22:00.728 D/DBINFOJOIN: Data for user User2, Name is Sue etc
    2021-09-17 11:22:00.728 D/DBINFOJOIN:    Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:22:00.728 D/DBINFOJOIN: Data for user User1, Name is Mary etc
    2021-09-17 11:22:00.728 D/DBINFOJOIN:    Post is post1 Type is xxx body is:-
                post1 blah
    2021-09-17 11:22:00.728 D/DBINFOJOIN:    Post is post3 Type is sth body is:-
                post3 blah
    

    完全过滤:-

    2021-09-17 11:22:00.738 D/DBINFOFULL: Data for user User2, Name is Sue etc
    2021-09-17 11:22:00.738 D/DBINFOFULL:    Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:22:00.738 D/DBINFOFULL: Data for user User1, Name is Mary etc
    2021-09-17 11:22:00.738 D/DBINFOFULL:    Post is post3 Type is sth body is:-
                post3 blah
    

    所有用户,但有过滤帖子

    2021-09-17 11:22:00.744 D/DBINFOPOST: Data for user User1, Name is Mary etc
    2021-09-17 11:22:00.744 D/DBINFOPOST:    Post is post3 Type is sth body is:-
                post3 blah
    2021-09-17 11:22:00.744 D/DBINFOPOST: Data for user User2, Name is Sue etc
    2021-09-17 11:22:00.744 D/DBINFOPOST:    Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:22:00.744 D/DBINFOPOST: Data for user User3, Name is Tom etc
    

    如果 PostEntity 列改回为 id 则结果为

    :-

    2021-09-17 11:27:00.661 D/DBINFOALL: Data for user User1, Name is Mary etc
    2021-09-17 11:27:00.661 D/DBINFOALL:     Post is post1 Type is xxx body is:-
                post1 blah
    2021-09-17 11:27:00.661 D/DBINFOALL:     Post is post3 Type is sth body is:-
                post3 blah
    2021-09-17 11:27:00.661 D/DBINFOALL: Data for user User2, Name is Sue etc
    2021-09-17 11:27:00.662 D/DBINFOALL:     Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:27:00.662 D/DBINFOALL: Data for user User3, Name is Tom etc
    2021-09-17 11:27:00.664 D/DBINFOJOIN: Data for user post2, Name is Sue etc
    2021-09-17 11:27:00.664 D/DBINFOJOIN: Data for user post3, Name is Mary etc
    2021-09-17 11:27:00.672 D/DBINFOFULL: Data for user post2, Name is Sue etc
    2021-09-17 11:27:00.672 D/DBINFOFULL: Data for user post3, Name is Mary etc
    2021-09-17 11:27:00.676 D/DBINFOPOST: Data for user User1, Name is Mary etc
    2021-09-17 11:27:00.676 D/DBINFOPOST:    Post is post3 Type is sth body is:-
                post3 blah
    2021-09-17 11:27:00.676 D/DBINFOPOST: Data for user User2, Name is Sue etc
    2021-09-17 11:27:00.676 D/DBINFOPOST:    Post is post2 Type is sth body is:-
                post2 blah
    2021-09-17 11:27:00.677 D/DBINFOPOST: Data for user User3, Name is Tom etc
    
    • 注意用户是帖子的 ID,因此没有基础帖子。
    • 您可以使用@Embedded(prefix = "a_suitable_prefix")。但是,您必须在查询中使用 AS 更改用户(前缀表)的列名,使用明确的列名要简单得多。
    • 第 4 次,返回所有用户但仅包含过滤后的帖子不受影响,因为它不使用 Data POJO,这是歧义导致用户 ID 成为帖子 ID 的地方。

    【讨论】:

    • 是的,这行得通!但是是否有任何语句可以在不手动执行此工作的情况下完成这项工作?我的意思是一个数据库调用。
    • 假设完全过滤的工作,那么没有(至少到目前为止)。我相信原因是 Room 是以对象为中心的,而数据库只是存储介质,并且获取不完整的对象(在你的情况下不是所有的帖子)被认为是错误的,也许是在“用对象做”的幌子下"。
    猜你喜欢
    • 1970-01-01
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多