'How to get relationship data with spring boot rest api?

I'm building a REST API with Spring Boot to retrieve boat information. I'm using Spring Data Rest and Spring Data JPA. When I get the data from the API, I don't know why the relationship data are not with the others informations.

Do I have to configure something in Spring to get the relationship with my data ?

Here is my file.

Boat entity:

@Entity
@Table(name="boat")
@Data
public class Boat {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "description")
    private String description;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "type_id", nullable = false)
    @JsonBackReference
    private BoatType type;

}

Boat type entity :

@Entity
@Table(name = "boat_type")
@Data
public class BoatType {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
    @JsonManagedReference
    private Set<Boat> boats;

}

Boat repository :

@CrossOrigin("http://localhost:4200")
public interface BoatRepository extends JpaRepository<Boat, Long> {
}

JSON response :

{
"_embedded": {
  "boats": [
    {
      "id": 1,
      "name": "Boat 1",
      "description": "A brief description of the boat 1",
      "_links": {
        "self": {
          "href": "http://localhost:8080/api/boats/1"
        },
        "boat": {
          "href": "http://localhost:8080/api/boats/1"
        },
        "type": {
          "href": "http://localhost:8080/api/boats/1/type"
        }
      }
    },
    ...
  ]

}

Result expected (with the type object too) :

{
"_embedded": {
  "boats": [
    {
      "id": 1,
      "name": "Boat 1",
      "description": "A brief description of the boat 1",
      "type": {
          "id": 1,
          "name": "Motorboats"
      },
      "_links": {
        "self": {
          "href": "http://localhost:8080/api/boats/1"
        },
        "boat": {
          "href": "http://localhost:8080/api/boats/1"
        },
        "type": {
          "href": "http://localhost:8080/api/boats/1/type"
        }
      }
    },
    ...
  ]

}

I think that the problem is related with Spring Data Rest because when i do the same app with my own controller and repository, i get the data I need.

Is there a way to "configure" spring data rest?



Solution 1:[1]

It seems like you've used @JsonBackReference and @JsonManagedReference the other way around, than you needed. You've put @JsonBackReference on the type field in your Boat class, whereas its documentation states:

[...] Linkage is handled such that the property annotated with this annotation is not serialized

So it seems like you need to put @JsonManagedReference annotation on it instead (see: JsonManagedReference documentation) and put @JsonBackReference on boats in your BoatType class.

Alternatively, you could consider using @JsonIdentityInfo instead. See: the documentation.

Also, this article might be helpful. It explains various ways to handle bidirectional relationships using Jackson.

Solution 2:[2]

Change @JsonManagedReference and @JsonBackReference to @JsonIgnoreProperties.

In your case:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
@JsonIgnoreProperties(value = {"type"})
private Set<Boat> boats;

and

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "type_id", nullable = false)
@JsonIgnoreProperties(value = {"boats"})
private BoatType type;

You will avoid the infinity loop in json result and get all reference objects (relationships).

Solution 3:[3]

The Boat response includes a uri to your BoatType resource by default since you defined a rest repository for your BoatType resource (docs)

To override this behaviour, define a projection to expose the boat type data (docs):

@Projection(name = "boatDetail", types = { Boat.class }) 
interface BoatDetail { 

  // ... all other fields you want included in the response

  BoatType getType(); 

}

Then include the projection as a query parameter:

{apiUrl}boats/1?projection=boatDetail

The response should now include the boat type data:

{
    "id": 1,
    "name": "Boat 1",
    "description": "A brief description of the boat 1",
    "type": {
        "id": 1,
        "name": "Motorboats"
    },
    "_links": {
        // ...
    }
}

To automatically include the projection on a collection of resources, use an excerpt (docs):

@RepositoryRestResource(excerptProjection = BoatDetail.class)
interface BoatRepository extends JpaRepository<Boat, Long> {}

Then the http response:

{
    "_embedded":{
        "boats":[
            {
                "id":1,
                "name":"Boat 1",
                "description":"A brief description of the boat 1",
                "type":{
                    "id":1,
                    "name":"Motorboats"
                },
                "_links":{
                    //...
                }
            },
            // ...
        ]
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Leroy Dunn