如何在房间数据库中查询和过滤关系实体

How to query and filter relation entity in room database

例如:

用户:

@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,

Post:

@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 我得到了想要的数据(一个用户和所有 post),但是我如何过滤特定类型的 post,比如 WHERE post.type = 'sth' 这可能吗?

but how can i filter the post for a specific type,

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

@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 和用户的 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
)

然后消除了歧义,数据对象 returned 包括所有具有 post 类型 sth 的相应用户的 posts .

如果您只想 return 编辑只有过滤后的 post 的数据对象,那么您必须绕过 Room 的 returning FULL/COMPLETE 技术相关对象。

如果您将 @Dao class 设为抽象 class 而不是接口,那么您将使用 @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
}

这会过滤数据对象 return,但会丢弃整个 PostEntity 列表(即每个 post,与过滤器无关),然后应用过滤后的 post。

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

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
}

即不对用户应用过滤,只对 posts.

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

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 获得用户的所有 post :-

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

所有用户,但过滤 posts

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
  • 注意用户是post的ID,因此没有基础post。
  • 你可以使用 @Embedded(prefix = "a_suitable_prefix")。但是,您随后必须使用 AS 更改查询中用户的列名(前缀为 table),使用明确的列名要简单得多。
  • 第 4 个,returning 所有用户但仅过滤 posts 不受影响,因为它不使用数据 POJO,这是导致用户 ID 出现歧义的地方post ID。