在房间查询中获取可变参数大小
Get vararg parameter size in Room Query
假设我有一个数据库,其中包含两个主要实体(歌曲和标签)以及它们之间的多对多关系。使用 Room,我想通过名称查询具有一系列标签(所有标签)的歌曲。
因此,在交叉引用 table (SongTagCrossRef) 中给出此示例数据:
Song
Tag
song1
tag1
song1
tag2
song1
tag3
song2
tag2
song3
tag2
song3
tag3
如果我输入 tag1 和 tag2,我希望查询 return 只有 song1,因为它是唯一与两者相关的歌曲。
我在相应的道中想出了这个@Query
:
@Query("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (:tagNames)
GROUP BY s.song_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM Tag WHERE name IN (:tagNames))
""")
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity>
由于我无法访问@Query
中的tagNames.size
,我不得不使用子查询来人为地获取它。这个子查询不应该太重,但以某种方式访问 tagNames.size
.
总是更好
在阅读 的答案后,我一直在尝试创建一个 @RawQuery
并从一个只需要 tagNames
的函数中调用它,大致如下:
@RawQuery
fun getSongsWithAllOfTheTagsByName(query: SupportSQLiteQuery): List<SongEntity>
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity> {
val query = SimpleSQLiteQuery("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (?)
GROUP BY s.song_id
HAVING COUNT(*) = ?
""", arrayOf(tagNames, tagNames.size))
return getSongsWithAllOfTheTagsByName(query)
}
(仅将 tagNames
转换为它实际可以吞咽的东西)
但我放弃了这种方法,因为我不想公开接受查询的函数。
是否有更简单、更优雅的方式来编写此查询?
我终于做到了,所以我想分享一下我的发现。它实际上不是很简单,但它确实有效。
通过 SQLite 文档,我发现了 JSON1 extension and more specifically the json_array() and json_array_length() 函数。
但是,要使用此扩展名,必须使用 CommonsWare points out in and Hooman summarises , Requery's standalone library,通过 RequerySQLiteOpenHelperFactory
。
总结:
build.gradle 文件
dependencies {
...
implementation 'com.github.requery:sqlite-android:3.36.0'
...
}
房间数据库class
Room.databaseBuilder(...)
...
.openHelperFactory(RequerySQLiteOpenHelperFactory())
...
.build()
道接口
@Query("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (:tagNames)
GROUP BY s.song_id
HAVING COUNT(*) = JSON_ARRAY_LENGTH(JSON_ARRAY(:tagNames))
""")
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity>
假设我有一个数据库,其中包含两个主要实体(歌曲和标签)以及它们之间的多对多关系。使用 Room,我想通过名称查询具有一系列标签(所有标签)的歌曲。
因此,在交叉引用 table (SongTagCrossRef) 中给出此示例数据:
Song | Tag |
---|---|
song1 | tag1 |
song1 | tag2 |
song1 | tag3 |
song2 | tag2 |
song3 | tag2 |
song3 | tag3 |
如果我输入 tag1 和 tag2,我希望查询 return 只有 song1,因为它是唯一与两者相关的歌曲。
我在相应的道中想出了这个@Query
:
@Query("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (:tagNames)
GROUP BY s.song_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM Tag WHERE name IN (:tagNames))
""")
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity>
由于我无法访问@Query
中的tagNames.size
,我不得不使用子查询来人为地获取它。这个子查询不应该太重,但以某种方式访问 tagNames.size
.
在阅读 @RawQuery
并从一个只需要 tagNames
的函数中调用它,大致如下:
@RawQuery
fun getSongsWithAllOfTheTagsByName(query: SupportSQLiteQuery): List<SongEntity>
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity> {
val query = SimpleSQLiteQuery("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (?)
GROUP BY s.song_id
HAVING COUNT(*) = ?
""", arrayOf(tagNames, tagNames.size))
return getSongsWithAllOfTheTagsByName(query)
}
(仅将 tagNames
转换为它实际可以吞咽的东西)
但我放弃了这种方法,因为我不想公开接受查询的函数。
是否有更简单、更优雅的方式来编写此查询?
我终于做到了,所以我想分享一下我的发现。它实际上不是很简单,但它确实有效。
通过 SQLite 文档,我发现了 JSON1 extension and more specifically the json_array() and json_array_length() 函数。
但是,要使用此扩展名,必须使用 CommonsWare points out in RequerySQLiteOpenHelperFactory
。
总结:
build.gradle 文件
dependencies {
...
implementation 'com.github.requery:sqlite-android:3.36.0'
...
}
房间数据库class
Room.databaseBuilder(...)
...
.openHelperFactory(RequerySQLiteOpenHelperFactory())
...
.build()
道接口
@Query("""
SELECT s.* FROM Song s
JOIN SongTagCrossRef st ON s.song_id = st.song_id
JOIN Tag t ON st.tag_id = t.tag_id
WHERE t.name IN (:tagNames)
GROUP BY s.song_id
HAVING COUNT(*) = JSON_ARRAY_LENGTH(JSON_ARRAY(:tagNames))
""")
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity>