Home > Blockchain >  Spring Data JPA - findAll does not return relations data
Spring Data JPA - findAll does not return relations data

Time:04-26

I have 2 tables - Screenings (ManyToOne) and Rooms (OneToMany). When I'm using findAll on rooms repository it returns me a json with screenings data but when Im doing that in opposite way (screenings.findAll) it does not returns rooms. In model Screening I have relation:

@ManyToOne
@JoinColumn(name = "room_id")
@JsonBackReference
private Room room;

and in Room model:

@OneToMany(mappedBy="room")
@JsonManagedReference
private List<Screening> screenings;

ScreeningService contains:

    @Autowired
    public ScreeningService(ScreeningRepository screeningRepository) {
        this.screeningRepository = screeningRepository;
    }

    public List<Screening> getScreenings(){
        return screeningRepository.findAll();
    }

and returned values are without Room model:

{
"id": 1,
"startDate": "2022-06-20T13:00:00.000 00:00",
"endDate": "2022-06-20T15:00:00.000 00:00"
},
{
"id": 2,
"startDate": "2022-06-20T13:15:00.000 00:00",
"endDate": "2022-06-20T15:15:00.000 00:00"
},

When Im doing that in opposite way and call

public List<Room> getRooms() {
        return roomRepository.findAll();
    }

result is exactly same as I want:

{
"id": 2,
"number": 2,
"screenings": [
{
"id": 2,
"startDate": "2022-06-20T13:15:00.000 00:00",
"endDate": "2022-06-20T15:15:00.000 00:00"
},
{
"id": 5,
"startDate": "2022-06-20T13:15:00.000 00:00",
"endDate": "2022-06-20T15:15:00.000 00:00"
},
{
"id": 7,
"startDate": "2022-06-20T18:15:00.000 00:00",
"endDate": "2022-06-20T21:15:00.000 00:00"
}
]
},

Is that possible to make or Im doing something wrong?

CodePudding user response:

The problem is on JSON serialization level.

You have a bidirectional relationship!
If you will serialize these objects by default without additional annotations(@JsonManagedReference, @JsonBackReference ) you will get StackOverflowError exception. The reason is that Jackson gets into infinite recursion by the bidirectional relationship.

So to resolve Jackson JSON infinite recursion problem you used @JsonManagedReference, @JsonBackReference. The main idea of annotations is that relation will be split into two parts: parent and child.
@JsonManagedReference is the parent (or "forward") part of the reference – the one that gets serialized normally.
@JsonBackReference is the child (or "back") part of the reference – it will be omitted from serialization.
So Jackson will serialize only one relation part and never gets into the infinity loop. You can see it in your example.
Alternatively, we can also use the @JsonIgnore annotation to simply ignore one of the sides of the relationship, thus breaking the chain.

In case you want to have the ability to serialize objects from both sides:
Solution: Annotation JsonIdentityInfo

Annotation used for indicating that values of annotated type or property should be serializing so that instances either contain additional object identifier (in addition actual object properties), or as a reference that consists of an object id that refers to a full serialization. In practice this is done by serializing the first instance as full object and object identity, and other references to the object as reference values.

Remove @JsonManagedReference, @JsonBackReference annotations from your entities.

Add the class level annotation to Screening entity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class Screening { ... }

Add the class level annotation to Room entity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class Room { ... }

The output of Room entity:

{
  "id": 2,
  "number": 2,
  "screenings": [
    {
      "id": 2,
      "startDate": "2022-06-20T13:15:00.000 00:00",
      "endDate": "2022-06-20T15:15:00.000 00:00",
      "room": 2
    },
    {
      "id": 5,
      "startDate": "2022-06-20T13:15:00.000 00:00",
      "endDate": "2022-06-20T15:15:00.000 00:00",
      "room": 2
    },
    {
      "id": 7,
      "startDate": "2022-06-20T18:15:00.000 00:00",
      "endDate": "2022-06-20T21:15:00.000 00:00",
      "room": 2
    }
  ]
}

The output of Screening entity:

{
  "id": 2,
  "startDate": "2022-06-20T13:15:00.000 00:00",
  "endDate": "2022-06-20T15:15:00.000 00:00",
  "room": {
    "id": 2,
    "number": 2,
    "screenings": [2,5,7]
  }
}

In case the default behavior of @JsonIdentityInfo does not fit your use case, you can create Custom Serializer and handle bidirectional problem on your own.

  • Related