简体   繁体   English

在 Spring 引导中使用 JPA 从具有 OneToMany 关系的实体中删除 object

[英]Delete object from an entity with a OneToMany relationship between both using JPA in Spring Boot

Good afternoon, I am working with a REST API in which I have a playlist that has many songs, for which I am using JPA and the benefits that allow me to make the relationships between the two.下午好,我正在使用 REST API,其中我有一个包含许多歌曲的播放列表,为此我使用 JPA 和允许我在两者之间建立关系的好处。 Now, if I want to delete a song already added to the PlayList, I can't do it, I show you my classes below现在,如果我想删除已经添加到播放列表中的歌曲,我做不到,我在下面向您展示我的课程

Class PlayList Class 播放列表

@Entity
@Table(name = "PLAY_LIST")
public class PlayList {

    @JsonIgnore
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    //@JsonView(View.Get.class)
    @Column(name="name")
    private String name;

    //@JsonView(View.Get.class)
    @Column(name="description")
    private String description;

    //@JsonView(View.Get.class)
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "playList")
    private List<Song> songs = new ArrayList<>();

    @Transient
    public void addSong(Song song) {
        song.setPlayList(this);
        songs.add(song);
    }

    @Transient
    public void removeSong(Song song) {
        songs.remove(song);
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }


    public List<Song> getSongs() {
        return this.songs;
    }

    public void setSongs(List<Song> songs) {
        this.songs = songs;
    }
    
}

Here I have the methods of adding songs and removing, however, removing is not working for me.在这里,我有添加歌曲和删除的方法,但是,删除对我不起作用。

Class Song Class 宋

Entity
@Table(name = "SONG")
public class Song{

    @JsonIgnore
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    //@JsonView(View.Create.class)
    @Column(name = "title")
    private String title;

    //@JsonView(View.Create.class)
    @Column(name = "artist")
    private String artist;

    //@JsonView(View.Create.class)
    @Column(name = "album")
    private String album;

    //@JsonView(View.Create.class)
    @Column(name = "year")
    private int year;

    /* 

    Fetch: Esta propiedad se utiliza para determinar cómo debe ser cargada la entidad.
    LAZY (perezoso): Indica que la relación solo se cargará cuando la propiedad sea leída por primera vez */
    //@JsonView(View.Get.class)
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PLAY_LIST_ID")
    private PlayList playList;


    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return this.artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getAlbum() {
        return this.album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public int getYear() {
        return this.year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public PlayList getPlayList() {
        return this.playList;
    }

    public void setPlayList(PlayList playList) {
        this.playList = playList;
    }
    
}

My class Controller我的 class Controller

@RestController
@RequestMapping("playlist")
public class PlayListController {

   @Autowired
   private PlayListService playListService;

   @Autowired
   private SongRepository songRepository;


   // Get playlist by id with songs belongs that playlist

   @GetMapping("/get/{id}")
   public Optional<PlayList> getPlayListByID(@PathVariable(value = "id") Long id) {

      Optional<PlayList> playList = playListService.getById(id);
      return playList;
   }

   @PostMapping("/create")
   public PlayList createPlayList(@RequestBody PlayListDTO playListDTO) {

      PlayList playList = new PlayList();

      playList.setName(playListDTO.getName());
      playList.setDescription(playListDTO.getDescription());
      playList.setSongs(new ArrayList<>());

      for (int x=0; x<playListDTO.getSongs().size(); x++) {

         Song songs=new Song();
         songs.setTitle(playListDTO.getSongs().get(x).getTitle());
         songs.setArtist(playListDTO.getSongs().get(x).getArtist());
         songs.setAlbum(playListDTO.getSongs().get(x).getAlbum());
         songs.setYear(playListDTO.getSongs().get(x).getYear());
         playList.addSong(songs);

      }
        return playListService.savePlayList(playList);
     }
   @PutMapping("/update/{id}")
   public PlayList updatePlayList(@PathVariable(value = "id") Long id,@RequestBody Song song){

      PlayList playList = playListService.getById(id).get();
      song.setPlayList(playList);
      playList.getSongs().add(song);
      playListService.savePlayList(playList);
      return playList;
   }
   
   @DeleteMapping("/delete/{id}")
   public PlayList deletePlayList(@PathVariable(value = "id") Long id,@RequestBody Song song){
      PlayList playList =playListService.getById(id).get();
      System.out.println(playList.getSongs());
      playList.removeSong(song);
      System.out.println(playList.getSongs());
      return playList;
   }

}

So I am storing the Playlist with its songs, method POST所以我用它的歌曲存储播放列表,方法 POST

{
    "name": "Lista 1",
    "description": "Lista de reproduccion 2020 spotify",
    "songs": [
        {
            "title": "Tan Enamorados",
            "artist": "CNCO",
            "album": "Tan Enamorados",
            "year": 2020
        },
        {
            "title": "Hawai",
            "artist": "Maluma",
            "album": "PAPI JUANCHO",
            "year": 2020
        }
    ]
}

Now, to eliminate I am passing the id of the PlayList and the Request of the song (object without the id of the song that is automatically created in BD), however, I cannot eliminate the song from the PlayList, and at the logs level it returns this doing a print in console现在,为了消除我正在传递播放列表的 id 和歌曲的请求(没有在 BD 中自动创建的歌曲的 id 的对象),但是,我无法从播放列表中消除歌曲,并且在日志级别它返回这个在控制台中打印

Example, I want to delete the following song:例如,我想删除以下歌曲:

在此处输入图像描述

however it is not removed and it returns the same list to me但是它没有被删除,它返回相同的列表给我

Am I missing something to be able to delete a song without having to delete the playlist?我是否错过了无需删除播放列表即可删除歌曲的内容?

It is not a very good idea to remove a song using all the songs list in the PlayList .使用PlayList中的所有歌曲列表删除一首歌曲并不是一个好主意。 There is not a join table for @OneToMany association. @OneToMany关联没有连接表。 So we can delete a song much simpler, using SONG table (this is the main reason why a join table for @OneToMany is not convienent).所以我们可以更简单地删除一首歌曲,使用SONG表(这是@OneToMany的连接表不方便的主要原因)。

You need a song id for that and you need to use CrudRepository.deleteById() method.为此,您需要一个歌曲 ID,并且需要使用CrudRepository.deleteById()方法。 You can use the full combination (title, artist, album, year) for that, but much simpler to add a song id to JSON.您可以为此使用完整组合(标题、艺术家、专辑、年份),但将歌曲 ID 添加到 JSON 会更简单。

Better to use this endpoint URL to delete a song最好用这个端点URL来删除一首歌

/{playListId}/songs/{songId}

You don't need delete part in the URL, you already use DELETE HTTP method.您不需要delete URL 中的部分,您已经使用了 DELETE HTTP 方法。

Why your code doesn't work为什么你的代码不起作用

  1. Incorrect using delete method from the list使用列表中的删除方法不正确
@Transient
public void removeSong(Song song) {
    songs.remove(song);
}

songs.remove() can't find song in the list, List.remove() finds a song by a reference. songs.remove()无法在列表中找到song ,List.remove( List.remove()通过引用找到歌曲。 It needs to have an open persistent context and get a song from it to have an ability to find it in the list.它需要有一个开放的持久上下文并从中获取一首歌曲才能在列表中找到它。

  1. Not using a transaction (opened persistent context) to let Hibernate know that a song was deleted and Hibernate has to update the database.不使用事务(打开的持久上下文)让 Hibernate 知道歌曲已被删除,并且 Hibernate 必须更新数据库。

So a valid scenario所以一个有效的场景

start @Transactional
  Spring starts a database transaction
  Spring opens a persistent context
  load PlayList
  load a song from the database (using id or other song attributes)
  delete a song from PlayList songs (or delete a song from PlayList songs using id)
end @Transactional
    
Hibernate flushes changes and delete a song in the database
persistent context is closed
database transaction is committed

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM