房间中的多对多关系返回空列表

问题描述 投票:0回答:1

所以,我有一个关于动漫应用程序的案例,其中一个流派可以是多个动漫,而一个动漫可以有多个流派。所以,我把它变成了

Many to Many Relationship
并使用
Room Database
将它们与交叉引用连接起来。

我的动漫实体:

@Entity(
    tableName = "anime",
    indices = [Index(value = ["mal_id"], unique = true)]
)
data class AnimeBasicEntity(

    @PrimaryKey
    @ColumnInfo(name = "mal_id")
    val malId: Int,

    // Other properties, not included because too long and might not related
)

我的类型实体:

@Entity(
    tableName = "genre",
    indices = [Index(value = ["mal_id"], unique = true)]
)
data class AnimeGenreEntity(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "mal_id")
    val malId: Int,

    // Other properties, not included because too long and might not related
)

还有我的交叉参考:

@Entity(
    tableName = "anime_genre_cross_reference",
    primaryKeys = ["anime_id", "genre_id"],
    foreignKeys = [
        ForeignKey(
            entity = AnimeBasicEntity::class,
            parentColumns = ["mal_id"],
            childColumns = ["anime_id"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = AnimeGenreEntity::class,
            parentColumns = ["mal_id"],
            childColumns = ["genre_id"],
            onDelete = ForeignKey.CASCADE
        ),
    ]
)
data class AnimeGenreCrossReference(

    @ColumnInfo(name = "anime_id", index = true)
    val animeId: Int,

    @ColumnInfo(name = "genre_id", index = true)
    val genreId: Int
)

我为返回结果做了一个包装,如下所示:

data class AnimeFullEntity(

    @Embedded
    val anime: AnimeBasicEntity,

    @Relation(
        parentColumn = "mal_id",
        entityColumn = "mal_id",
        associateBy = Junction(
            value = AnimeGenreCrossReference::class,
            parentColumn = "anime_id",
            entityColumn = "genre_id"
        )
    )
    val genres: List<AnimeGenreEntity>
)

这是我的

DAO

    @Transaction
    @Query("SELECT * FROM anime WHERE mal_id =:malId LIMIT 1")
    suspend fun getAnimeByAnimeId(malId: Int): AnimeFullEntity

我尝试像这样使用

CROSS JOIN
INNER JOIN

    @Transaction
    @Query(
        "SELECT * FROM anime " +
                "CROSS JOIN anime_genre_cross_reference ON anime_id=:malId " +
                "WHERE mal_id =:malId"
    )
    suspend fun getAnimeByAnimeId(malId: Int): AnimeFullEntity

查询成功,但查询期间发生某种非致命错误。我似乎无法追踪它,因为它说该位置超出了我的代码行(错误发生在第 207 行,而我的代码在 150 左右)。但是,当我返回到上一个查询时,查询返回一个空列表。

android kotlin android-sqlite android-room
1个回答
0
投票

我认为您可能会对 whwre 使用 mal_id 值感到困惑,因为您通过使其等于传递的值来限制 JOIN,而实际上 JOIN 可能应该是当一列等于另一列时。

也许考虑对第二个查询进行以下微妙的更改:-

SELECT * FROM anime CROSS JOIN anime_genre_cross_reference ON anime_id=mal_id WHERE mal_id =:malId

- i.e. `ON anime_id (column name) = mal_id (column name NOT passed value)

- Which may sometimes result in nothing matching the JOIN

但是,当使用

@Query
注释的 SELECT 时这样做没有任何好处,因为 Room 只关心 AnimeBasic,然后它使用 @Relation 通过结果的交叉引用(关联)表获取 AnimeGenres动漫完整。

您似乎也有重复的索引。当使用

@Primary
键时,这是一个唯一索引。在复合索引的情况下,索引的第一列根据该列,因此不需要在该列上也有索引。在第二列上有一个索引可能是个好主意(如果省略,Room 会发出警告)。

示范

考虑以下

@Dao
带注释的界面:-

@Dao
interface AllDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(animeBasicEntity: AnimeBasicEntity): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(animeGenreEntity: AnimeGenreEntity): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(animeGenreCrossReference: AnimeGenreCrossReference): Long

    @Transaction
    @Query("SELECT * FROM anime WHERE mal_id =:malId LIMIT 1")
    /*suspend*/ fun getAnimeByAnimeId(malId: Int): AnimeFullEntity

    @Transaction
    @Query("SELECT * FROM anime CROSS JOIN anime_genre_cross_reference ON anime_id=mal_id WHERE mal_id =:malId")
    fun getAnimeByAnimeIdOther(malId: Int): AnimeFullEntity

    @Transaction
    @Query("SELECT * FROM anime CROSS JOIN anime_genre_cross_reference ON anime_id=:malId WHERE mal_id =:malId")
    fun getAnimeByAnimeIdOriginal(malId: Int): AnimeFullEntity
}

然后是一些活动代码:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = TheDatabase.getInstance(this)
        dao = db.getAllDAOs()

        val ab01id = dao.insert(AnimeBasicEntity(10))
        val ab02id = dao.insert(AnimeBasicEntity(20))
        val ag01id = dao.insert(AnimeGenreEntity(100))
        val ag02id = dao.insert(AnimeGenreEntity(200))
        dao.insert(AnimeGenreCrossReference(ab01id.toInt(),ag01id.toInt()))
        dao.insert(AnimeGenreCrossReference(ab01id.toInt(),ag02id.toInt()))

        var af = dao.getAnimeByAnimeId(ab01id.toInt())
        var sb = StringBuilder()
        for (g in af.genres) {
            sb.append("\n\t${g.malId}")
        }
        Log.d("DBINFO","AF has abid as ${af.anime.malId} and has ${af.genres.size} Genres. They are:-${sb}")
        af = dao.getAnimeByAnimeIdOther(ab01id.toInt())
        sb = StringBuilder()
        for (g in af.genres) {
            sb.append("\n\t${g.malId}")
        }
        Log.d("DBINFO","AF has abid as ${af.anime.malId} and has ${af.genres.size} Genres. They are:-${sb}")
        af = dao.getAnimeByAnimeIdOriginal(ab01id.toInt())
        sb = StringBuilder()
        for (g in af.genres) {
            sb.append("\n\t${g.malId}")
        }
        Log.d("DBINFO","AF has abid as ${af.anime.malId} and has ${af.genres.size} Genres. They are:-${sb}")
    }
}

运行时日志包括:-

2024-01-29 16:46:39.934 D/DBINFO: AF has abid as 10 and has 2 Genres. They are:-
        100
        200
2024-01-29 16:46:39.941 D/DBINFO: AF has abid as 10 and has 2 Genres. They are:-
        100
        200
  • 即前两个已经产生了预期的结果,最后一个(原始的 CROSS JOIN)没有产生任何结果。也就是说,细微的差别会带来不同。
© www.soinside.com 2019 - 2024. All rights reserved.