简体   繁体   中英

@ManyToMany is not working from Child-to-Parent using Spring/JPA/REST

I am relatively new to Java/Spring. I have created a very simple test application using Spring framework. I have been experimenting with @JsonIgnore/@JsonManagedReference/@JsonBackReference. And I have tried many suggestions that are online, But I cannot get @ManyToMany annotation to work bi-directional.

I define two entities Playlist, and Song. And there is ManyToMany relation between them.

I define the Playlist Entity as:

@Entity
@Table(name="playlist")
public class Playlist implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;


    private Integer playlistId;

    @Id
    @GeneratedValue
    @Column(name="playlist_id")
    public Integer getPlaylistId() {
        return playlistId;
    }

    public void setPlaylistId(Integer playlistId) {
        this.playlistId = playlistId;
    }

    private String name;

    private Set<Song> songs = new HashSet<Song>(0);

    @ManyToMany(cascade={CascadeType.ALL})
    @JoinTable(name = "playlist_song",
            joinColumns = {@JoinColumn(name = "f_playlist_id",  referencedColumnName = "playlist_id")},
            inverseJoinColumns = {@JoinColumn(name = "f_song_id", referencedColumnName = "song_id")}
            )
    @JsonManagedReference
    public Set<Song> getSongs() {
        return songs;
    }

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


    @Column(name="name")
    public String getName() {
        return name;
    }

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


}

second entity Song as:

@Entity
@Table(name="song")
public class Song implements Serializable{

    public Song() {
    }

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private Integer songIdentifier;
    private String name;
    private Set<Playlist> playlists = new HashSet<Playlist>(0);


    @Id
    @Column(name="song_id")
    @GeneratedValue
    public Integer getSongIdentifier() {
        return songIdentifier;
    }

    public void setSongIdentifier(Integer songIdentifier) {
        this.songIdentifier = songIdentifier;
    }


    @Column(name="name")
    public String getName() {
        return name;
    }

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

    @ManyToMany(mappedBy="songs")
    @JsonBackReference
    public Set<Playlist> getPlaylists() {
        return playlists;
    }

    public void setPlaylists(Set<Playlist> playlists) {
        this.playlists = playlists;
    }

}

Define @Service as:

@Service
public class SongServiceImpl implements SongServiceDao {

    @Autowired
    private SongRepo songRepo;

    @Override
    public Collection<Song> findAll() {

        Collection <Song> allsongs = (Collection<Song>) songRepo.findAll();
        return allsongs;
    }

}

I define @Controller as

@RestController
public class M2MController {

    @Autowired
    SongServiceDao songService;

     @RequestMapping(
                value = "/test/songs",
                method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
        public ResponseEntity<Collection<Song>> getSongs() {

            Collection<Song> songs = songService.findAll();

            return new ResponseEntity<Collection<Song>>(songs, HttpStatus.OK);

        }

     @Autowired
     PlaylistServiceDao playlistService;

         @RequestMapping(
                    value = "/test/playlists",
                    method = RequestMethod.GET,
                    produces = MediaType.APPLICATION_JSON_VALUE)
            public ResponseEntity<Collection<Playlist>> getPlaylists() {

                Collection<Playlist> playlists = playlistService.findAll();

                return new ResponseEntity<Collection<Playlist>>(playlists, HttpStatus.OK);

            }

}

I define schema in MySql as

-- -----------------------------------------------------
-- Table `m2mDb`.`playlist`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `m2mDb`.`playlist` (
  `playlist_id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NULL,
  PRIMARY KEY (`playlist_id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `m2mDb`.`song`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `m2mDb`.`song` (
  `song_id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NULL,
  PRIMARY KEY (`song_id`),
  UNIQUE INDEX `songId_UNIQUE` (`song_id` ASC))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `m2mDb`.`playlist_song`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `m2mDb`.`playlist_song` (
  `f_playlist_id` INT NOT NULL,
  `f_song_id` INT NOT NULL,
  INDEX `toSong_idx` (`f_song_id` ASC),
  INDEX `toPlaylist_idx` (`f_playlist_id` ASC),
  PRIMARY KEY (`f_playlist_id`, `f_song_id`),
  INDEX `secondary` (`f_song_id` ASC, `f_playlist_id` ASC),
  CONSTRAINT `toPlaylist`
    FOREIGN KEY (`f_playlist_id`)
    REFERENCES `m2mDb`.`playlist` (`playlist_id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `toSong`
    FOREIGN KEY (`f_song_id`)
    REFERENCES `m2mDb`.`song` (`song_id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

When I run this then I get following results:

/test/songs returns:

[
  {
    "songIdentifier": 1,
    "name": "song one"
  },
  {
    "songIdentifier": 2,
    "name": "song 2"
  },
  {
    "songIdentifier": 3,
    "name": "song 3"
  }
]

and /test/playlists returns:

[
  {
    "playlistId": 1,
    "name": "list 1",
    "songs": [
      {
        "songIdentifier": 2,
        "name": "song 2"
      },
      {
        "songIdentifier": 3,
        "name": "song 3"
      },
      {
        "songIdentifier": 1,
        "name": "song one"
      }
    ]
  },
  {
    "playlistId": 2,
    "name": "list 2",
    "songs": [
      {
        "songIdentifier": 3,
        "name": "song 3"
      },
      {
        "songIdentifier": 1,
        "name": "song one"
      }
    ]
  }

Playlists returns correct result. I am expecting songs to have list of all playlists as well.

If anyone can shed some light on where I am messing it up, I will highly appreciate.

When you use @JsonManagedReference and @JsonBackReference , what happens it that during the serialization of a Song , Jackson won't serialize the list of PlayList because that is already being serialized at another level in your XML.

What you could look into using is @JsonIdentityInfo

That would at least allow you to have JSON to output the PlayList array but using the identifiers associated to the play lists that it contains.

[
  {
    "playlistId": 1,
    "name": "list 1",
    "songs": [
      {
        "songIdentifier": 2,
        "name": "song 2"
        "playLists": [1, 2]
      }
    ]
  },

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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