I have implemented the following entities:
Teacher:
@Getter
@Setter
@ToString
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false)
private Long id;
private String name;
@ManyToMany(cascade = CascadeType.MERGE)
@JoinTable(
name = "teacher_timeslot",
joinColumns = @JoinColumn(name = "teacher_id"),
inverseJoinColumns = @JoinColumn(name = "timeslot_id")
)
@ToString.Exclude
private Set<Timeslot> timeslots = new HashSet<>(); //teacher preferred timeslots
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Teacher teacher = (Teacher) o;
return id != null && Objects.equals(id, teacher.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
Timeslot:
//ignore the JsonIdentity as I need it to another class (Timetable to be more specific)
@JsonIdentityInfo(scope = Timeslot.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Getter
@Setter
@ToString
//@RequiredArgsConstructor
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Timeslot{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false)
@PlanningId
private Long id;
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Timeslot timeslot = (Timeslot) o;
return id != null && Objects.equals(id, timeslot.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
And those are the following JSON payload that I am using for saving/get a Teacher or Timeslot:
Save Timeslot:
{"id":3,"dayOfWeek":"MONDAY","startTime":"14:00:00","endTime":"16:00:00"}
Get Timeslots:
[
{
"id": 1,
"dayOfWeek": "MONDAY",
"startTime": "10:00:00",
"endTime": "12:00:00"
},
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
},
{
"id": 3,
"dayOfWeek": "MONDAY",
"startTime": "14:00:00",
"endTime": "16:00:00"
}
]
Save/Get Teacher:
([){
"id": 1,
"name": "Ben",
"timeslots": [
{
"id": 1,
"dayOfWeek": "MONDAY",
"startTime": "10:00:00",
"endTime": "12:00:00"
},
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
}
]
}(])
If I want to add another Teacher:
{
"id": 2,
"name": "Alex",
"timeslots": [
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
},
{
"id": 3,
"dayOfWeek": "MONDAY",
"startTime": "14:00:00",
"endTime": "16:00:00"
}
]
}
And I perform a getAllTeachers() request I receive the following:
[
{
"id": 1,
"name": "Ben",
"timeslots": [
{
"id": 1,
"dayOfWeek": "MONDAY",
"startTime": "10:00:00",
"endTime": "12:00:00"
},
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
}
]
},
{
"id": 2,
"name": "Alex",
"timeslots": [
2, // the problem
{
"id": 3,
"dayOfWeek": "MONDAY",
"startTime": "14:00:00",
"endTime": "16:00:00"
}
]
}
]
instead of:
[
{
"id": 1,
"name": "Ben",
"timeslots": [
{
"id": 1,
"dayOfWeek": "MONDAY",
"startTime": "10:00:00",
"endTime": "12:00:00"
},
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
}
]
},
{
"id": 2,
"name": "Alex",
"timeslots": [
{
"id": 2,
"dayOfWeek": "MONDAY",
"startTime": "12:00:00",
"endTime": "14:00:00"
},
{
"id": 3,
"dayOfWeek": "MONDAY",
"startTime": "14:00:00",
"endTime": "16:00:00"
}
]
}
]
How can I solve this issue? What am I missing here? Basically, I want to replicate the following sequence of code while ensuring the CRUD functionality:
Timeslot wednesday12to14 = new Timeslot(nextTimeslotId++, DayOfWeek.WEDNESDAY, LocalTime.of(12, 0), LocalTime.of(14, 0));
Teacher ben = new Teacher(nextTeacherId++, "Ben", List.of(tuesday12to14, tuesday14to16,
tuesday16to18, tuesday18to20, tuesday20to22, wednesday12to14, wednesday14to16, wednesday18to20, wednesday20to22));
Any help will be appreciated! Feel free to edit if I didn't formulate the question correctly.
I tried to following things:
None of them works.
This is my Teacher Service implementation:
@Service
@RequiredArgsConstructor
@Slf4j
public class TeacherService {
private final TeacherRepo teacherRepo;
public List<Teacher> getAllTeachers() {
return teacherRepo.findAllByOrderByIdAsc();
}
public Teacher createTeacher(Teacher teacher) {
return teacherRepo.save(teacher);
}
//update and delete methods
}
I managed to solve this issue by creating a TeacherDTO and TimeslotDTO and by using ModelMapper on the get Method:
DTOs:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TimeslotDTO {
private Long id;
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TeacherDTO {
private Long id;
private String name;
private Set<TimeslotDTO> timeslots;
}
TeacherService:
private final ModelMapper modelMapper;
public List<TeacherDTO> getAllTeachers() {
List<Teacher> retrievedTeachers = teacherRepo.findAllTeachersWithTimeslotsOrderedById();
return retrievedTeachers.stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
}
private TeacherDTO convertToDTO(Teacher teacher) {
TeacherDTO teacherDTO = modelMapper.map(teacher, TeacherDTO.class);
Set<TimeslotDTO> timeslotDTOs = teacher.getTimeslots().stream()
.map(timeslot -> modelMapper.map(timeslot, TimeslotDTO.class))
.collect(Collectors.toSet());
teacherDTO.setTimeslots(timeslotDTOs);
return teacherDTO;
}