Home > Software engineering >  Spring Boot - Cannot use get from Postman after adding a song: NoSuchElementException (Spring) and I
Spring Boot - Cannot use get from Postman after adding a song: NoSuchElementException (Spring) and I

Time:12-09

our teacher asked us to create a spring boot app to manage songs and albums with a one-to-many-relationship using DTOs, but we need to do the mapping manually in the controller, we cannot use external libraries. The point is, no matter how I try to change the methods, it always results in an error on Spring (we use PostgreSQL and in pgadmin I can see the data without problems).

For instance, with the code I am sharing right now I can initially use get on songs and albums, but if I add a new song then the get method results in a NoSuchElementException,

with this line in particular: at com.newplaylist.controller.SongController.getSongs(SongController.java:55)

which points to this line: songs.add(new SongDTO((Song) iterator.next(), iterator.next().getAlbum()));

and I also receive a 500 Internal server error on Postman. Thanks in advance for the help, if needed I can add the complete exception trace.

  • SongDTO

import com.newplaylist.entity.Album;
import com.newplaylist.entity.Song;

public class SongDTO {

    private Integer id;
    private String author, title;
    private AlbumDTO album;
    
    public SongDTO() {
            
    }
    
    public SongDTO(Song song) { 
        this.id = song.getId();
        this.author = song.getAuthor();
        this.title = song.getTitle();
    }
    
    public SongDTO(Song song, Album album) {    
        this.id = song.getId();
        this.author = song.getAuthor();
        this.title = song.getTitle();
        this.album = new AlbumDTO(album, true);
    }
    
    // getters and setters...

}
  • AlbumDTO

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.newplaylist.entity.Album;
import com.newplaylist.entity.Song;

public class AlbumDTO {
    
    private Integer id;
    private String name;
    private String production;
    private Set<SongDTO> songs;
    
    public AlbumDTO() {
    }
    
    public AlbumDTO(Album album) {
        this.id = album.getId();
        this.name = album.getName();
        this.production = album.getProduction();
    }
    
    public AlbumDTO(Album album, boolean lazy) {
        this.id = album.getId();
        this.name = album.getName();
        this.production = album.getProduction();
        songs = new HashSet<SongDTO>();
        album.getSongs();
        
        if (!lazy) {
            for (Iterator<Song> iterator = album.getSongs().iterator(); iterator.hasNext();) {
                songs.add(new SongDTO((Song) iterator.next()));
            }
        }
    }

    // getters and setters...

}
  • SongController
package com.newplaylist.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.newplaylist.dto.AlbumDTO;
import com.newplaylist.dto.SongDTO;
import com.newplaylist.entity.Album;
import com.newplaylist.entity.Song;
import com.newplaylist.service.SongService;

@RestController
@RequestMapping("/song")
public class SongController {

    @Autowired
    SongService songService;
    
    public void init() {
    }
    
    @PostMapping("/add")
    public ResponseEntity<?> addSong(@RequestBody Song song) {
        try {
            return new ResponseEntity<SongDTO>(new SongDTO(songService.addSong(song), song.getAlbum()), HttpStatus.CREATED);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<Object>(e.getStackTrace(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @GetMapping("/all")
    public ResponseEntity<Collection<SongDTO>> getSongs() {
        Collection<SongDTO> songs = new ArrayList<>();
        
        try {
            for (Iterator<Song> iterator = songService.getSongs().iterator(); iterator.hasNext();) {
                songs.add(new SongDTO((Song) iterator.next(), iterator.next().getAlbum()));
            }
            
            return new ResponseEntity<Collection<SongDTO>>(songs, HttpStatus.OK);
            
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
}
  • AlbumController
package com.newplaylist.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.newplaylist.dto.AlbumDTO;
import com.newplaylist.dto.SongDTO;
import com.newplaylist.entity.Album;
import com.newplaylist.entity.Song;
import com.newplaylist.service.AlbumService;

@RestController
@RequestMapping("/album")
public class AlbumController {

    @Autowired
    AlbumService albumService;
    
    @PostMapping("/add")
    public ResponseEntity<?> addAlbum(@RequestBody Album album) {
        try {
            return new ResponseEntity<AlbumDTO>(new AlbumDTO(albumService.addAlbum(album), true), HttpStatus.CREATED);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<Object>(e.getStackTrace(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @GetMapping("/all")
    public ResponseEntity<Collection<AlbumDTO>> getAlbums() {
        Collection<AlbumDTO> albums = new ArrayList<>();
        
        try {
            for (Iterator<Album> iterator = albumService.getAlbums().iterator(); iterator.hasNext();) {
                albums.add(new AlbumDTO((Album) iterator.next(), false));
            }

            return new ResponseEntity<Collection<AlbumDTO>>(albums, HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        
    }

}
  • Album (entity)
package com.newplaylist.entity;

import java.util.Set;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;

@Entity
public class Album {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String production;
    
    
    //@JsonBackReference        // per scegliere quale reference stampare, altrimenti andrebbe in loop infinito
    @OneToMany(mappedBy = "album", cascade = CascadeType.ALL)
    private Set<Song> songs;
    
    public Album() {
        super();
    }

    public Album(Integer id, String name, String production, Set<Song> songs) {
        super();
        this.id = id;
        this.name = name;
        this.production = production;
        this.songs = songs;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getProduction() {
        return production;
    }

    public void setProduction(String production) {
        this.production = production;
    }

    public Set<Song> getSongs() {
        return songs;
    }

    public void setSongs(Set<Song> songs) {
        this.songs = songs;
    }
    
}
  • Song (entity)

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;

@Entity
public class Song {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String author, title;
    
//  @OneToOne
//  @JoinColumn(name = "album")     // campo album della tab song. questo campo contiene un id che si collega all'entità Album, perciò fai l'inner join con essa.
//  private Album album;
    
//  @ManyToOne
//  @JoinColumn(name = "playlist")
//  private Playlist playlist;
    
    @ManyToOne
    @JoinColumn(name = "album", nullable = false)
    //@JsonManagedReference
    private Album album;
    
    public Song() {}

    public Song(Integer id, String author, String title) {
        super();
        this.id = id;
        this.author = author;
        this.title = title;
    }

    public Song(Integer id, String author, String title, Album album) {
        super();
        this.id = id;
        this.author = author;
        this.title = title;
        this.album = album;
    }

    // getters and setters
    
}

I believe the problem is in one of these classes, but if you need something else I can upload the project on GitHub.

CodePudding user response:

You need something like this

 try {
        for (Iterator<Song> iterator = songService.getSongs().iterator(); iterator.hasNext();) {
            Song currentSong = iterator.next();
            songs.add(new SongDTO(currentSong, currentSong.getAlbum()));
        }

Calling iterator.next() will always fetch a new Song and this means that iterator.next().getAlbum() will fetch the album that corresponds to the next song in the collection and not the current one that you've already fetched with the first call to iterator.next(). Furthemore, it will cause an exception if you have an odd number of songs.

  • Related