I have just started using Spring boot and I am using the default repository api to retrieve db data as json. I have added a @ManyToOne relation to my Song and Artist Entity.
But now i am not getting the Artist object in my json response from the server and its not really clear to me, how I can include it without missing out on the pagination functions from the PagingAndSorting repository.
I am using the spring-data-rest-jpa.
My response now looks like:
"_embedded": {
"songs": [
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"_links": {
"self": {
"href": "http://localhost:8080/api/songs/1"
},
"song": {
"href": "http://localhost:8080/api/songs/1"
},
"artist": {
"href": "http://localhost:8080/api/songs/1/artist"
}
}
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/songs?page=0&size=1"
},
"self": {
"href": "http://localhost:8080/api/songs?size=1"
},
"next": {
"href": "http://localhost:8080/api/songs?page=1&size=1"
},
"last": {
"href": "http://localhost:8080/api/songs?page=19&size=1"
},
"profile": {
"href": "http://localhost:8080/api/profile/songs"
}
},
"page": {
"size": 1,
"totalElements": 20,
"totalPages": 20,
"number": 0
}
}
But I want it rather to be like this:
"_embedded": {
"songs": [
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"artist": {
"id": 1,
"name": "Artistname"
}
"_links": {
"self": {
"href": "http://localhost:8080/api/songs/1"
},
"song": {
"href": "http://localhost:8080/api/songs/1"
},
"artist": {
"href": "http://localhost:8080/api/songs/1/artist"
}
}
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/songs?page=0&size=1"
},
"self": {
"href": "http://localhost:8080/api/songs?size=1"
},
"next": {
"href": "http://localhost:8080/api/songs?page=1&size=1"
},
"last": {
"href": "http://localhost:8080/api/songs?page=19&size=1"
},
"profile": {
"href": "http://localhost:8080/api/profile/songs"
}
},
"page": {
"size": 1,
"totalElements": 20,
"totalPages": 20,
"number": 0
}
}
Song.java
@Getter
@Setter
@Entity
@Table(name = "song")
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, unique = true)
private Long id;
@NotNull
@NotBlank(message = "The song has to have a title")
private String title;
@NotNull
@NotBlank(message = "The song has to have a genre")
private String genre;
@NotNull
@Min(value = 1, message = "The song has to have a song length in seconds")
private int length;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "artist_id", referencedColumnName = "id")
private Artist artist;
/* @Version
private long version;*/
public Song() {
}
public Song(String title, Artist artist, String genre, int length) {
this.title = title;
this.artist = artist;
this.genre = genre;
this.length = length;
}
public void setArtist(Artist artist) {
this.artist = artist;
}
public Artist getArtist() {
return artist;
}
}
Artist.java
@Getter
@Setter
@Entity
@Table(name = "artist")
public class Artist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@NotNull
@NotBlank(message = "The artist has to have a name")
private String name;
@JsonIgnore
@OneToMany(mappedBy = "artist")
private List<Song> songs;
public Artist() {
}
public Artist(String name) {
this.name = name;
}
For the sake of testing I wrote a method in my SongController:
@GetMapping
List<Song> getSongs() {
return songRepository.findAll();
}
The result includes the Artist object, but won't have any pagination to it. How could I include it?
Json Result:
[
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"artist": {
"id": 1,
"name": "ArtistName"
}
}
]
CodePudding user response:
After all your useful suggestions I have found an answer: I have changed the return type of my Method in my controller to Page and made use of the PageRequest Class which looks like this:
@GetMapping
public Page<Song> getSongs(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) {
PageRequest pr = PageRequest.of(page, size);
return songRepository.findAll(pr);
}
Also used some defaultvalues to avoid some exceptions ;)
CodePudding user response:
Use either @JsonIdentityInfo or @JsonIgnore and remove @JsonBackReference. Here is the example with @JsonIgnore
public class Artist {
public Long id;
public String name;
public List<Song> songs;
}
public class Song {
public Long id;
public String title;
public String genre;
public int length;
@JsonIgnore
public Artist artist;
}
@JsonManagedReference or @JsonBackReference won't help in this case (read more on https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion):
(...) we can use the @JsonIgnore annotation to simply ignore one of the sides of the relationship, thus breaking the chain. [my addition: the chain of recurrent calls]
Here is the example with @JsonIdentityInfo:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Artist { ... }
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Song { ... }