【问题标题】:How to insert data to many to many table - room(Sql) android如何将数据插入多对多表 - room(Sql) android
【发布时间】:2020-09-11 11:03:40
【问题描述】:

我创建了这个表,但我不知道如何将数据添加到 playListWithSongs 表中。

@Entity
data class SongEntity(
  var songId : Long? = null,
  var name: String? = null,
 )
@Entity
data class playlistEntity(
  var playListId : Long? = null,
  var playlistName: String? = null
)
@Entity
data class playListWithSongs(
     var playlistId: Long,
     var songId: Long
)

这里如何将 ids 插入到 playListWithSongs 表中?

但就我而言,播放列表可能包含相同的歌曲或为空。甚至歌曲都会出现在一个播放列表中,没有播放列表或多个播放列表,所以我如何插入 PlayListWithSongs 实体。由于两个 id 都是主键,我的播放列表将有零首歌曲,因此将有 null 并且包含重复(这里的歌曲列表和当然大小不相似)

@Insert
fun insert(playListSong: playListWithSongs)

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertPlayList(playList: PlayListEntity?)

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCourse(song: SongsEntity?)

@Transaction
@Query("SELECT * FROM playListEntity")
fun getAll(): LiveData<List<PlayListWithSong>>

【问题讨论】:

    标签: java android mysql kotlin android-room


    【解决方案1】:

    如果您在所有dao 类中将插入函数的返回类型定义为Long,您将能够获取插入项的id,然后使用此id 插入PlayListWithSongs。例如:

    @Dao
    interface SongDao {
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        suspend fun insert(entity: Song): Long
    }
    

    更新:

    请注意,SongPlaylist 是两个不同的实体。因此,当您想将 Song 分配给 Playlist(这意味着在 playListWithSongs 中添加一个包含它们的 id 的新行)时,目标歌曲和播放列表应该存在于 db 中。

    我的意思是您永远不会尝试将歌曲添加到尚不存在的播放列表中。因此,在创建播放列表的第一刻,没有为其分配歌曲。这意味着playListWithSongs表中没有新创建的playlistId的行。

    综上所述,很明显,当您要将歌曲分配到播放列表时,您应该同时拥有两个 id。另外,最好不要使用这对作为playListWithSongs 的主键。让我们看看我为这个主题编写的代码:

    @Entity(
        tableName = "playListWithSongs",
        foreignKeys = [
            ForeignKey(
                entity = Song::class,
                parentColumns = ["songId"],
                childColumns = ["songId"],
                onDelete = CASCADE
            ),
            ForeignKey(
                entity = Playlist::class,
                parentColumns = ["playlistId"],
                childColumns = ["playlistId"],
                onDelete = CASCADE
            )
        ],
        indices = [
            Index(
                value = ["songId", "playlistId"],
                unique = true
            )
        ]
    )
    data class PlayListWithSongs(
        @PrimaryKey(autoGenerate = true) val id: Int? = null,
        @ColumnInfo(name = "songId") val songId: Long,
        @ColumnInfo(name = "playlistId") val playlistId: Long
    )
    

    正如您在上面的代码中看到的,如果一首歌曲或播放列表被删除,它在playListWithSongs 上的所有分配也会被删除(由于CASCADE)。所以不用担心未使用的行和重复(注意,songId 和 playlistId 的组合被定义为唯一的)。

    【讨论】:

    • 但在我的情况下,播放列表可能有相同的歌曲或为空。甚至歌曲都会出现在一个播放列表、没有播放列表或多个播放列表中,所以我如何插入 PlayListWithSongs 实体。
    • 由于两个 id 都是主键,并且我将有零首歌曲作为播放列表,因此会出现 null 并且包含重复(这里的歌曲列表和课程列表大小不相似)
    • @14Programming:我已经更新了答案。请检查一下。
    • 我建议您相信这种结构:) 每个播放列表可以包含零到任意数量的歌曲。此外,每首歌曲可以属于零到任意数量的播放列表。它正确地提供了多对多关系,没有任何重复。请注意,重复是指重复的 [playlistId, songId] 对,例如 [1, 5] 和 [1, 5],由于 Index(value = ["songId", "playlistId"], unique = true ) 而无法存在。
    • 没问题,随意。请分享插入和检索的代码。
    【解决方案2】:

    OTOH,您可以在没有映射的情况下做到这一点。创建两个实体,一个用于歌曲,另一个用于带有歌曲的播放列表,而不是映射,将歌曲 id 作为列表对象保留在播放列表中,并相应地从播放列表中查询带有 id 的歌曲表。这是实现这一目标的方法 - https://medium.com/@toddcookevt/android-room-storing-lists-of-objects-766cca57e3f9

    尽管如此,我确实更喜欢 @aminography 在他的回答中详细阐述的常规映射,以便您可以利用 CASCADE 操作将父键上的删除或更新操作传播给每个依赖项子键。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-13
      • 2015-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-16
      • 1970-01-01
      相关资源
      最近更新 更多