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.