Issue
I am new to Java Spring and I am trying to find a way to serialize nested JPA entities with bidirectional relationships in different JSON results, depending on the Controller's access point.
Relationships:
User - One to Many - Booking
Booking - Many to Many - Room (booked rooms)
Hotel- One to Many - Room
The Classes:
User.java
@Entity @Table(name ="user") @Getter @Setter
public class User {
@Id
@SequenceGenerator(name="user_sequence", sequenceName="user_sequence",allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
private Long id;
private String first_name;
private String last_name;
@OneToMany(mappedBy = "user")
private List<Booking> bookings = new ArrayList<>();
}
Hotel.java
@Entity @Table(name="hotel") @Getter @Setter
public class Hotel {
@Id
@SequenceGenerator(name="hotel_sequence",sequenceName="hotel_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hotel_sequence")
private Long id;
private String name;
@OneToMany(mappedBy = "hotel")
private List<Room> rooms = new ArrayList<>();
}
Room.java
@Entity @Table(name ="room") @Getter @Setter
public class Room {
@Id
@SequenceGenerator( name="room_sequence",sequenceName="room_sequence",allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "room_sequence")
private Long id;
private String type;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="hotel_id", referencedColumnName = "id")
private Hotel hotel;
@ManyToMany(mappedBy = "rooms")
private List<Booking> booking = new ArrayList<>();
}
Booking.java
@Entity @Table(name="booking") @Getter @Setter
public class Booking {
@Id
@SequenceGenerator(name="booking_sequence",sequenceName="booking_sequence",allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "booking_sequence")
private Long id;
private double price;
private String info;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="user_id", referencedColumnName = "id")
private User user;
@ManyToMany
@JoinTable(
name="booking_rooms",
joinColumns = @JoinColumn(name = "booking_id"),
inverseJoinColumns = @JoinColumn(name="room_id")
)
private List<Room> rooms = new ArrayList<>();
}
I need the following JSON results:
A user with his bookings (including the rooms in the booking)
A hotel including the rooms (excluding the bookings of the rooms)
A room's bookings (including the user(excluding his bookings))
I've read the documentation for Jackson here: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
and I tried with most of the approaches, but couldn't succeed completing all of the above mentioned results. I tried to make custom serializers, but as I said I am pretty new to Spring and didn't find a way to serialize the entities for each scenario. It just seems "too nested" to succeed.
The only solution I have is to create DTO classes for the results I want and just 'collect' the data by looping through the repositories, but it is a pretty dirty solution, as I will have more complicated structure of relationships in the future with more entities, thus I will have A LOT of DTO classes.
Is there a better way of conducting the relationships?
What would be the best approach for this specific case?
Solution
Given that your cases are not straightforward that could be easily tackled by the solutions mentioned in the article you shared, I would definitely go for DTOs. Why? Let me list a couple of advantages:
- Clear definition of whatever you want to include in the response to the caller, without custom serializers,
@JsonView
s,@JsonIgnore
or other not easy to follow and read tricks. - Restructure of the public model is possible so that it fits better the needs of the clients. This structure might not be the same structure that works better to model your business logic.
- Inner model becomes decoupled from the public model your API consumers know, making it possible to rework your inner model and still keep the same public model.
There are, however, some disadvantages such as having to additionally map entities to DTOs. You can tackle this one by using mapping libraries such as MapStruct. Nevertheless, the advantages clearly outweigh the disadvantages and thus you definitely should go with DTOs.
Answered By - João Dias