Home > Back-end >  Java Spring boot @ManyToOne relations missing from JSON result
Java Spring boot @ManyToOne relations missing from JSON result

Time:10-21

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 { ... }
  • Related