如何在带有 Room 的 RawQuery 中使用联接?

Posted

技术标签:

【中文标题】如何在带有 Room 的 RawQuery 中使用联接?【英文标题】:How can I use joins in a RawQuery with Room? 【发布时间】:2021-10-09 14:05:38 【问题描述】:

我有大约 15 个可能的条件查询。我想避免使用this answer,因为我不想更新 DAO 以添加新选项。

我的数据如下:

@Entity
data class Author(
    @PrimaryKey
    val id: UUID,
    val name: Date,
    val birthday: Date,
)

@Entity
data class Book(
    @PrimaryKey
    val id: UUID,
    val authorId: UUID,
    val name: String,
    val published: Date,
)

data class AuthorWithBooks(
    @Embedded val author: Author,
    @Relation(parentColumn = "id", entityColumn = "authorId", entity = Book::class)
    val books: List<Books>,
)

没有外键引用,因为这些数据都是由另一个来源支持的,并且这本书有可能在作者之前保存。

DAO 类似于:

@Dao
interface AuthorDao 
    .
    .
    .
    @Transaction
    @RawQuery(observedEntities = [AuthorWithBooks::class])
    fun observeByQuery(query: SupportSQLiteQuery): PagingSource<Int, AuthorWithBooks>

存储库是这样的:

class AuthorRepository(
    private val dao: AuthorDao
) 
    fun search(arguments: List<Pair<String, Any?>>): Flow<PagingData<Author>> 
        val queryBuilder = SupportSQLiteQueryBuilder.builder("Author JOIN Book")
        val conditions = arguments.map  it.first 
        val whereArgs = arguments.mapNotNull  it.second 
        queryBuilder.selection(conditions.joinToString(" AND "), whereArgs.toTypedArray())
        val query = queryBuilder.create()
        return Pager(
            config = PagingConfig(pageSize = 10),
            pagingSourceFactory =  dao.observeByQuery(query) 
        ).flow
    

我对此的访问类似于:

val results = authorRepository(listOf(
    Pair("Book.name LIKE ?", pattern),
    Pair("Author.birthday < ?", bornBefore)
))

这返回一个AuthorWithBooks 的次数我数不过来。是否可以使用RawQuery 来做我想做的事?这里有什么错误?如果没有人看到这个问题,我可能会创建一个简单的项目来演示这一点。

【问题讨论】:

【参考方案1】:

我不知道,但请查看此链接,它会对您有所帮助,

https://gokulbalakrishnan.medium.com/join-queries-room-persistence-library-f0527e7b0da1

https://developer.android.com/training/data-storage/room/accessing-data#multiple-tables

查询多个表 您的某些查询可能需要访问多个表来计算结果。您可以在 SQL 查询中使用 JOIN 子句来引用多个表。

以下代码定义了一个将三个表连接在一起以将当前借给特定用户的书籍返回的方法:

@Query(
    "SELECT * FROM book " +
    "INNER JOIN loan ON loan.book_id = book.id " +
    "INNER JOIN user ON user.id = loan.user_id " +
    "WHERE user.name LIKE :userName"
)
fun findBooksBorrowedByNameSync(userName: String): List<Book>

【讨论】:

感谢您的回答,但它并没有真正解决我的问题。当使用具有关系的嵌入式实体时,Room 会生成查询以获取相关数据。在这种情况下使用连接是不合适的。我可以重新构建我的数据以使用适合返回结果的 POJO,然后也许这会起作用。请参阅我的答案以了解我采用的解决方案。【参考方案2】:

问题在于 JOIN。

从构建器中删除连接,例如:

class AuthorRepository(
    private val dao: AuthorDao
) 
    fun search(arguments: List<Pair<String, Any?>>): Flow<PagingData<Author>> 
        val queryBuilder = SupportSQLiteQueryBuilder.builder("Author")
        val conditions = arguments.map  it.first 
        val whereArgs = arguments.mapNotNull  it.second 
        queryBuilder.selection(conditions.joinToString(" AND "), whereArgs.toTypedArray())
        val query = queryBuilder.create()
        return Pager(
            config = PagingConfig(pageSize = 10),
            pagingSourceFactory =  dao.observeByQuery(query) 
        ).flow
    

并在搜索参数中使用子查询,例如:

val results = authorRepository(listOf(
    Pair("EXISTS (SELECT 1 FROM Book WHERE (Book.authorId = Author.id) AND (Book.name LIKE ?)", pattern),
    Pair("Author.birthday < ?", bornBefore)
))

【讨论】:

以上是关于如何在带有 Room 的 RawQuery 中使用联接?的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 Android 的 Room 使用 UUID 作为主键

如何使用带有 Kotlin 的 Room 和 moshi 持久化带有 JSON 数组的 JSON 对象

如何将带有 Android Room 的应用添加为使用 makefile 编译的系统应用?

Room:使用 fallbackToDestructiveMigrationFrom() 进行迁移如何工作?

如何在 Activity 中使用 Room 和 LiveData 删除 LiveData<Object>?

rawQuery(查询,selectionArgs)