1

遵循多对多关系的 Android 文档,可在此处找到:定义多对多关系

我有以下实体

@Entity
class Song(
    @PrimaryKey val songId: Long,
    @ColumnInfo val name: String,
    @ColumnInfo val path: Uri,
)
@Entity
data class PlaylistInfo(
    @ColumnInfo var name: String,
    @PrimaryKey(autoGenerate = true) val playlistId: Long = 0,
)

然后我有一个实体,它是两者之间的交叉引用。

@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

最后,我有一个类将两者结合起来,使用PlaylistSongCrossRef作为连接点。

data class Playlist(
    @Embedded val info: PlaylistInfo,
    @Relation(
        parentColumn = "playlistId",
        entityColumn = "songId",
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: MutableList<Song>
)

然后我在 Dao 中定义了读取查询。

这是文档结束的地方。我现在正试图弄清楚如何插入、更新或删除Playlist.

我尝试在 Dao 中定义以 aPlaylist作为参数并用 、 或 注释的函数@Insert@Update@Delete它们给了我以下错误:

错误:参数的类型必须是使用@Entity 注释的类或其集合/数组。com.user.musicplayer.models.entities.Playlist 播放列表

我不确定从这里去哪里。如果有人可以将我推荐给可以帮助我的资源或提供有效的实现,那将不胜感激。

4

1 回答 1

1

我现在正试图弄清楚如何插入、更新或删除播放列表

播放列表是一个 POJO,旨在(当与 Embedded/@Relation 一起使用时)用于从数据库中提取数据,而不是用于修改数据库中的数据。即这是一种方式。

但是,如果它不是数据类而是类(我认为),那么您可以添加函数来执行此类操作。例如

class Playlist {

    @Embedded
    var playlistInfo: PlaylistInfo? = null
    @Relation(
        parentColumn = "playlistId",
        entityColumn = "songId",
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    var songs: MutableList<Song>? = null

    fun deletePlayList(dao: AllDao, playlist: Playlist, deleteOrphanedSongs: Boolean = false) {
        val currentPlaylistInfo: PlaylistInfo? = playlist.playlistInfo
        if (currentPlaylistInfo != null) {
            dao.deleteByPlayListId(currentPlaylistInfo.playlistId)
            dao.delete(currentPlaylistInfo)
            if (deleteOrphanedSongs) {
                dao.deleteManySongs(playlist.songs!!)
            }
        }
    }
}

您可以使用提取的播放列表执行的操作是删除播放列表和歌曲,例如:-

@Delete()
fun deletePlayListInfoAndSongs(playlistInfo: PlaylistInfo, songs: List<Song>): Int
  • 即使用提取的播放列表,您将拥有嵌入式播放列表信息和相关的歌曲

但是,请注意,上述内容不会删除关联的PlaylistSongCrossRef行,因此您最终会得到可能有问题的孤儿。

您可能希望考虑定义外键并使用 onDelete 和/或 onUpdate 操作进行 CASCADE 删除。

示例/演示

以下是基于您的实体(为方便起见将歌曲路径更改为字符串而不是 URI)和上述播放列表类的示例。

供您考虑的道是:-

@Dao
interface AllDao {
    @Insert
    fun insert(playlistInfo: PlaylistInfo): Long
    @Insert
    fun insertManyPlaylistInfos(playlistInfoList: List<PlaylistInfo>): LongArray
    @Insert
    fun insert(song: Song): Long
    @Insert
    fun insertManySongs(songList: List<Song>): LongArray
    @Insert
    fun insert(playlistSongCrossRef: PlaylistSongCrossRef): Long
    @Insert
    fun insertManyPlaylistSongCrossrefs(playlistSongCrossRefList: List<PlaylistSongCrossRef>): LongArray

    @Query("SELECT * FROM song")
    fun getAllSongs(): List<Song>
    @Query("SELECT * FROM song WHERE song.name = :songname LIMIT 1")
    fun getFirstSongByName(songname: String): Song
    @Query("SELECT * FROM playlistInfo")
    fun getAllPlaylistInfos(): List<PlaylistInfo>
    @Query("SELECT * FROM playlistinfo WHERE playlistinfo.name = :playlistname LIMIT 1")
    fun getFirstPlaylistInfoByName(playlistname: String): PlaylistInfo
    @Query("SELECT * FROM playlistsongcrossref")
    fun getAllPlaylistSongCrossRefs(): List<PlaylistSongCrossRef>
    @Transaction
    @Query("SELECT * FROM playlistinfo")
    fun getAllPlaylists(): List<Playlist>

    @Query("SELECT count(*) FROM song")
    fun getNumberOfSongs(): Long
    @Query("SELECT count(*) FROM playlistinfo")
    fun getNumberOfPlaylistInfos(): Long
    @Query("SELECT count(*) FROM playlistsongcrossref")
    fun getNumberOfSongsInPlaylists(): Long
    @Update
    fun update(song: Song)
    @Update
    fun update(playlistInfo: PlaylistInfo)

    /* Suggested NEVER to use */
    @Update
    fun update(playlistSongCrossRef: PlaylistSongCrossRef)

    @Delete
    fun delete(playlistInfo: PlaylistInfo): Int
    @Delete()
    fun delete(song: Song): Int
    @Delete
    fun deleteManySongs(songs: List<Song>): Int
    @Delete
    fun delete(playlistSongCrossRef: PlaylistSongCrossRef): Int
    @Delete()
    fun deletePlayListInfoAndSongs(playlistInfo: PlaylistInfo, songs: List<Song>): Int
    //@Delete
    //fun deletePlayListInfoAndSongs(playlist: Playlist): Int
    @Query("DELETE FROM playlistsongcrossref WHERE playlistId = :playlistId")
    fun deleteByPlayListId(playlistId: Long): Int
    @Query("DELETE FROM playlistsongcrossref WHERE playlistId = :playlistId AND songId = :songId")
    fun deleteSongFromPlaylist(playlistId: Long, songId: Long)
}

也许在一个显示更新、删除和插入示例的活动中考虑以下内容:-

class MainActivity : AppCompatActivity() {

    private val TAG = "PLDBINFO"
    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    val songList = listOf<Song>(
        Song(1L,"My Song","x"),
        Song(2L,"Your Song","Y"),
        Song(3L,"Her Song","Z"),
        Song(4L,"His Song","A"),
        Song(5L,"Their Song","B"),
        Song(6L,"Nobody's Song","C")
    )
    val playlistInfoList = listOf<PlaylistInfo>(
        PlaylistInfo("My PlayList"),
        PlaylistInfo("Your PlayList"),
        PlaylistInfo("Her PlayList")
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Get the Database and the Dao
        db = Room.databaseBuilder(this,TheDatabase::class.java,"playlist.db")
            .allowMainThreadQueries()
            .build()
        dao = db.getDao()

        // Add all the Playlist and Songs and add every Song to all the Playlists
        // ONLY IF no data exists
        if (dao.getNumberOfPlaylistInfos() + dao.getNumberOfPlaylistInfos() + dao.getNumberOfSongsInPlaylists() < 1) {
            dao.insertManySongs(songList)
            dao.insertManyPlaylistInfos(playlistInfoList)
            for(p: PlaylistInfo in dao.getAllPlaylistInfos()) {
                for(s: Song in dao.getAllSongs()) {
                    dao.insert(PlaylistSongCrossRef(p.playlistId,s.songId))
                }
            }
        }

        // Write the counts to the Log
        logStats()

        // Extract All Core Objects
        var allPlaylistInfos = dao.getAllPlaylists()
        var allSongs = dao.getAllSongs()
        var allPlaylists = dao.getAllPlaylists()
        var allPlaylistSongCrossRefs = dao.getAllPlaylistSongCrossRefs()
        Log.d(TAG,"Extracted PlaylistInfos = ${allPlaylistInfos.size} " +
                "Extacted Songs = ${allSongs.size} " +
                "Extracted Playlists = ${allPlaylists.size} " +
                "Extracted PlaylistSongCrossRefs = ${allPlaylistSongCrossRefs.size}"
        )


        // Add a new song and also put it into the My PlayList playlist
        dao.insert(PlaylistSongCrossRef(dao.getFirstPlaylistInfoByName("My PlayList").playlistId,dao.insert(Song(10L,"Bert's Song","D"))))
        // ASame as above BUT using function getPlaylistInfoIdByName below that gets the ID
        dao.insert(PlaylistSongCrossRef(getPlaylistInfoIdByName("My PlayList"),dao.insert(Song(20L,"Mary's Song","E"))))

        //
        var otherPlTodelete = dao.getFirstPlaylistInfoByName("Her PlayList")
        for(pl: Playlist in allPlaylistInfos) {
            /* Best to not use this
            if (pl.playlistInfo!!.playlistId == plToDelete.playlistId ) {
                dao.deletePlayListInfoAndSongs(pl.playlistInfo!!,pl.songs!!)
            }

             */
            if (pl.playlistInfo!!.playlistId == otherPlTodelete.playlistId) {
                pl.deletePlayList(dao,pl,false) /* best to only use false */
            }
        }

        var playlistToUpdate = dao.getFirstPlaylistInfoByName("Your PlayList")
        playlistToUpdate.name = "PlayList that is for Your but it was Your PlayList"
        dao.update(playlistToUpdate)

        logStats()
    }

    private fun logStats() {
        Log.d(TAG," Playlist Count = " + dao.getNumberOfPlaylistInfos()
                + " Song Count = " + dao.getNumberOfSongs()
                + " Song/Playlist count = " + dao.getNumberOfSongsInPlaylists()
        )
    }

    private fun getSongIdByName(songName: String): Long {
        return dao.getFirstSongByName(songName).songId
    }
    private fun getPlaylistInfoIdByName(playlistInfoName: String): Long {
        return dao.getFirstPlaylistInfoByName(playlistInfoName).playlistId
    }
}

首先,如果不存在数据(通过查询获取行数的示例),则添加 3 个播放列表和 6 首歌曲,每个播放列表包含所有歌曲(因此 18 个交叉引用行)。

  • 显然这不是真实的情况(坚果它很方便/易于测试)

然后统计信息(每个表的行输出到日志)。

然后提取所有类型(PlaylistInfo's、Song's、PlaylistSongCrossref's 和 PlayList's)并显示计数(应与统计数据相符)。

然后,两首新歌曲不仅作为歌曲添加,而且还添加到名为“我的播放列表”的播放列表中

根据名称找到名为“Her PlayList”的播放列表信息(又名播放列表)。它与 PlayListSongCrossRef 一起被删除,因此不会通过 Playlists 类中的函数留下孤立的歌曲。

然后名为“Your PlayList”的播放列表被命名为“PlayList that is for Your but it was Your PlayList”。

于 2021-05-08T15:22:42.290 回答