Skip to content
This repository was archived by the owner on Jan 28, 2026. It is now read-only.

Commit 3ecfdf1

Browse files
author
ge85riz
committed
🔧 refactor(backend): use a new DTO to transfer filtered previous matches to avoid unintended entity cascade issues when transferring filtered previous match data
1 parent 4b4ea66 commit 3ecfdf1

7 files changed

Lines changed: 77 additions & 57 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.itestra.eep.dtos.constraintSolver;
2+
3+
import com.itestra.eep.models.EmployeeParticipation;
4+
import com.itestra.eep.models.PreviousMatch;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
8+
import java.util.Set;
9+
10+
@Getter
11+
@AllArgsConstructor
12+
public class EmployeeParticipationDTO {
13+
14+
EmployeeParticipation employeeParticipation;
15+
16+
Set<PreviousMatch.PreviousMatchId> filteredPreviousMatches;
17+
18+
}

‎backend/src/main/java/com/itestra/eep/mappers/EmployeeParticipationMapper.java‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import com.itestra.eep.dtos.EmployeeParticipationDetailsDTO;
44
import com.itestra.eep.dtos.constraintSolver.ConstraintSolverDTO;
5+
import com.itestra.eep.dtos.constraintSolver.EmployeeParticipationDTO;
56
import com.itestra.eep.models.EmployeeParticipation;
7+
import com.itestra.eep.models.PreviousMatch;
68
import org.mapstruct.Mapper;
79
import org.mapstruct.Mapping;
810
import org.mapstruct.MappingConstants;
@@ -16,19 +18,20 @@
1618
public interface EmployeeParticipationMapper {
1719

1820

19-
@Mapping(source = "employee.id", target = "profileId")
20-
@Mapping(source = "employee.profile.name", target = "employeeProfileName")
21-
@Mapping(source = "employee.profile.lastName", target = "employeeProfileLastName")
22-
@Mapping(source = "employee.profile.gender", target = "employeeProfileGender")
23-
@Mapping(source = "employee.employmentStartDate", target = "employeeEmploymentStartDate")
24-
@Mapping(source = "employee.location", target = "employeeLocation")
21+
@Mapping(source = "employeeParticipation.employee.id", target = "profileId")
22+
@Mapping(source = "employeeParticipation.employee.profile.name", target = "employeeProfileName")
23+
@Mapping(source = "employeeParticipation.employee.profile.lastName", target = "employeeProfileLastName")
24+
@Mapping(source = "employeeParticipation.employee.profile.gender", target = "employeeProfileGender")
25+
@Mapping(source = "employeeParticipation.employee.employmentStartDate", target = "employeeEmploymentStartDate")
26+
@Mapping(source = "employeeParticipation.employee.location", target = "employeeLocation")
27+
@Mapping(source = "employeeParticipation.guestCount", target = "guestCount")
2528
@Mapping(target = "lastNeighbourhood", expression = "java(findPreviouslyMatchedEmployeeIds(employeeParticipation))")
26-
ConstraintSolverDTO toConstraintSolverDTO(EmployeeParticipation employeeParticipation);
29+
ConstraintSolverDTO toConstraintSolverDTO(EmployeeParticipationDTO employeeParticipation);
2730

28-
List<ConstraintSolverDTO> toConstraintSolverDTO(Set<EmployeeParticipation> employeeParticipations);
31+
List<ConstraintSolverDTO> toConstraintSolverDTO(Set<EmployeeParticipationDTO> employeeParticipations);
2932

30-
default UUID[] findPreviouslyMatchedEmployeeIds(EmployeeParticipation employeeParticipation) {
31-
return employeeParticipation.getEmployee().getPreviousMatches().stream().map(pm -> pm.getId().getSecondEmployeeId()).toArray(UUID[]::new);
33+
default UUID[] findPreviouslyMatchedEmployeeIds(EmployeeParticipationDTO employeeParticipation) {
34+
return employeeParticipation.getFilteredPreviousMatches().stream().map(PreviousMatch.PreviousMatchId::getSecondEmployeeId).toArray(UUID[]::new);
3235
}
3336

3437
@Mapping(source = "id", target = "id")

‎backend/src/main/java/com/itestra/eep/models/Employee.java‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ public class Employee {
3737
@OneToMany(mappedBy = "employee", orphanRemoval = true, fetch = FetchType.LAZY)
3838
private Set<EmployeeParticipation> participations = new HashSet<>();
3939

40-
// Do not Cascade changes or Orphan Remove. We modify this association
41-
// when we perform Previous Matches Filtering. That change shouldn't be cascaded.
42-
@OneToMany(mappedBy = "employee", fetch = FetchType.LAZY, cascade = {}, orphanRemoval = false)
40+
@OneToMany(mappedBy = "employee", fetch = FetchType.LAZY)
4341
private Set<PreviousMatch> previousMatches = new LinkedHashSet<>();
4442

4543
// TODO this is fetched EAGERLY for some reason during Employee Batch Upsert

‎backend/src/main/java/com/itestra/eep/models/PreviousMatch.java‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,5 @@ public PreviousMatchId(UUID firstEmployeeId, UUID secondEmployeeId, UUID eventId
4343
this.secondEmployeeId = secondEmployeeId;
4444
this.eventId = eventId;
4545
}
46-
47-
@ManyToOne(fetch = FetchType.LAZY, optional = false)
48-
@JoinColumn(name = "event_id", nullable = false, insertable = false, updatable = false)
49-
private Event event;
50-
51-
public void setEvent(Event event) {
52-
this.event = event;
53-
this.eventId = event.getId();
54-
}
5546
}
5647
}

‎backend/src/main/java/com/itestra/eep/repositories/custom/PreviousMatchesRepositoryCustom.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.itestra.eep.repositories.custom;
22

3-
import com.itestra.eep.models.EmployeeParticipation;
3+
import com.itestra.eep.dtos.constraintSolver.EmployeeParticipationDTO;
44
import com.itestra.eep.models.PreviousMatch;
55
import org.springframework.stereotype.Repository;
66

@@ -17,5 +17,5 @@ public interface PreviousMatchesRepositoryCustom {
1717

1818
void batchInsertPreviousMatches(List<PreviousMatch.PreviousMatchId> matches);
1919

20-
Set<EmployeeParticipation> getEmployeeParticipationsWithFilteredPreviousMatches(UUID eventId, int cutoffYear);
20+
Set<EmployeeParticipationDTO> getEmployeeParticipationsWithFilteredPreviousMatches(UUID event, LocalDateTime eventDate, int cutoffYear);
2121
}

‎backend/src/main/java/com/itestra/eep/repositories/custom/impl/PreviousMatchesRepositoryCustomImpl.java‎

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
package com.itestra.eep.repositories.custom.impl;
22

3+
import com.itestra.eep.dtos.constraintSolver.EmployeeParticipationDTO;
34
import com.itestra.eep.exceptions.EventNotFoundException;
45
import com.itestra.eep.models.Employee;
5-
import com.itestra.eep.models.EmployeeParticipation;
66
import com.itestra.eep.models.Event;
77
import com.itestra.eep.models.PreviousMatch;
8+
import com.itestra.eep.repositories.EmployeeParticipationRepository;
89
import com.itestra.eep.repositories.EventRepository;
910
import com.itestra.eep.repositories.custom.PreviousMatchesRepositoryCustom;
10-
import jakarta.persistence.EntityManager;
1111
import lombok.RequiredArgsConstructor;
12-
import org.hibernate.Session;
1312
import org.springframework.jdbc.core.JdbcTemplate;
1413
import org.springframework.stereotype.Repository;
1514
import org.springframework.transaction.annotation.Transactional;
1615

1716
import java.sql.PreparedStatement;
1817
import java.time.LocalDateTime;
19-
import java.util.LinkedHashSet;
20-
import java.util.List;
21-
import java.util.Set;
22-
import java.util.UUID;
18+
import java.util.*;
2319
import java.util.stream.Collectors;
2420

2521
@Repository
@@ -28,7 +24,22 @@ public class PreviousMatchesRepositoryCustomImpl implements PreviousMatchesRepos
2824

2925
private final JdbcTemplate jdbcTemplate;
3026
private final EventRepository eventRepository;
31-
private final EntityManager entityManager;
27+
private final EmployeeParticipationRepository employeeParticipationRepository;
28+
29+
30+
@Override
31+
public Map<UUID, List<UUID>> findEmployeeIdsSittingWithAcquaintances(UUID eventId, int cutoffYear) {
32+
Event event = eventRepository.findById(eventId).orElseThrow(EventNotFoundException::new);
33+
34+
LocalDateTime cutoffDate = event.getDate().minusYears(cutoffYear);
35+
Set<UUID> eventsToConsider = eventRepository.findEventsByDateBetween(cutoffDate, event.getDate().minusMinutes(1));
36+
37+
List<Object[]> previousMatchesPairs = employeeParticipationRepository
38+
.findEmployeePairsOfCurrentEventThatAreAcquaintedFromPreviousEvents(eventsToConsider, eventId);
39+
40+
return getAcquaintedEmployeesMap(previousMatchesPairs);
41+
42+
}
3243

3344
@Override
3445
public void batchInsertPreviousMatches(List<PreviousMatch.PreviousMatchId> matches) {
@@ -53,35 +64,32 @@ public void batchInsertPreviousMatches(List<PreviousMatch.PreviousMatchId> match
5364

5465
@Override
5566
@Transactional(readOnly = true)
56-
public Set<EmployeeParticipation> getEmployeeParticipationsWithFilteredPreviousMatches(UUID eventId, int cutoffYear) {
57-
58-
Session session = entityManager.unwrap(Session.class);
59-
session.setDefaultReadOnly(true);
67+
public Set<EmployeeParticipationDTO> getEmployeeParticipationsWithFilteredPreviousMatches(UUID eventId, LocalDateTime eventDate, int cutoffYear) {
6068

6169
// we fetch all relevant data with entity graph to reduce DB round trips.
6270
Event event = eventRepository.findByIdJoinedWithPreviousMatches(eventId).orElseThrow(EventNotFoundException::new);
6371

64-
LocalDateTime eventDate = event.getDate();
6572
LocalDateTime cutoffDate = eventDate.minusYears(cutoffYear);
6673

74+
Set<UUID> eventsToConsider = eventRepository.findEventsByDateBetween(cutoffDate, eventDate);
75+
76+
Set<EmployeeParticipationDTO> employeeParticipationDTOs = new HashSet<>();
77+
6778
event.getEmployeeParticipations().forEach(ep -> {
6879
Employee emp = ep.getEmployee();
6980
UUID empId = emp.getId();
7081

71-
72-
Set<PreviousMatch> filteredMatches = emp.getPreviousMatches().stream()
73-
.filter(pm -> {
74-
LocalDateTime pmDate = pm.getId().getEvent().getDate();
75-
return pmDate.isBefore(eventDate) &&
76-
pmDate.isAfter(cutoffDate) &&
77-
empId.equals(pm.getId().getFirstEmployeeId());
78-
})
82+
Set<PreviousMatch.PreviousMatchId> filteredMatches = emp.getPreviousMatches().stream()
83+
.filter(pm ->
84+
eventsToConsider.contains(pm.getId().getEventId()) &&
85+
empId.equals(pm.getId().getFirstEmployeeId()))
86+
.map(PreviousMatch::getId)
7987
.collect(Collectors.toCollection(LinkedHashSet::new));
8088

81-
emp.getPreviousMatches().retainAll(filteredMatches);
89+
employeeParticipationDTOs.add(new EmployeeParticipationDTO(ep, filteredMatches));
8290
});
8391

84-
return event.getEmployeeParticipations();
92+
return employeeParticipationDTOs;
8593
}
8694

8795
private Map<UUID, List<UUID>> getAcquaintedEmployeesMap(List<Object[]> pairs) {

‎backend/src/main/java/com/itestra/eep/services/impl/SeatAllocationServiceImpl.java‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.itestra.eep.dtos.SeatAllocationDetailsDTO;
77
import com.itestra.eep.dtos.constraintSolver.ConstraintSolverDTO;
88
import com.itestra.eep.dtos.constraintSolver.ConstraintSolverTableDTO;
9+
import com.itestra.eep.dtos.constraintSolver.EmployeeParticipationDTO;
910
import com.itestra.eep.dtos.constraintSolver.StageMapDTO;
1011
import com.itestra.eep.exceptions.*;
1112
import com.itestra.eep.mappers.EmployeeParticipationMapper;
@@ -149,10 +150,11 @@ public void performTableBasedSeatAllocation(UUID eventId, StageMapDTO stageMap)
149150
throw new ParticipantOfPastEventException();
150151
}
151152

152-
Set<EmployeeParticipation> employeeParticipations =
153-
previousMatchesRepository.getEmployeeParticipationsWithFilteredPreviousMatches(eventId, CUT_OFF_YEAR);
153+
Set<EmployeeParticipationDTO> employeeParticipations =
154+
previousMatchesRepository.getEmployeeParticipationsWithFilteredPreviousMatches(eventId, event.getDate(), CUT_OFF_YEAR);
154155

155-
List<ConstraintSolverDTO> formattedData = employeeParticipationMapper.toConstraintSolverDTO(employeeParticipations);
156+
List<ConstraintSolverDTO> formattedData = employeeParticipationMapper
157+
.toConstraintSolverDTO(employeeParticipations);
156158

157159
Map<UUID, List<UUID>> tablesAndTheirSeats = stageMap.getSeatMap().entrySet().stream()
158160
.collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue().keySet())));
@@ -167,9 +169,9 @@ public void performTableBasedSeatAllocation(UUID eventId, StageMapDTO stageMap)
167169
List<ConstraintSolverDTO> solved = objectMapper.readValue(fileReader, new TypeReference<>() {
168170
});
169171

170-
Map<UUID, EmployeeParticipation> existingEmployeeParticipationsMap = employeeParticipations
172+
Map<UUID, EmployeeParticipationDTO> existingEmployeeParticipationsMap = employeeParticipations
171173
.stream()
172-
.collect(Collectors.toMap(ep -> ep.getEmployee().getId(), Function.identity()));
174+
.collect(Collectors.toMap(ep -> ep.getEmployeeParticipation().getEmployee().getId(), Function.identity()));
173175

174176

175177
Map<UUID, List<UUID>> tableToEmployeesMap = new HashMap<>();
@@ -178,20 +180,20 @@ public void performTableBasedSeatAllocation(UUID eventId, StageMapDTO stageMap)
178180
Map<UUID, UUID> visitorParticipationToChairMap = new HashMap<>();
179181

180182
solved.forEach(solvedConstraint -> {
181-
EmployeeParticipation employeeParticipation = existingEmployeeParticipationsMap.get(solvedConstraint.getProfileId());
182-
VisitorParticipation[] visitorParticipations = employeeParticipation.getVisitorParticipations().toArray(new VisitorParticipation[0]);
183-
for (int j = 0; j < (solvedConstraint.getTableIds()).length; j++) {
183+
EmployeeParticipationDTO epDTO = existingEmployeeParticipationsMap.get(solvedConstraint.getProfileId());
184+
VisitorParticipation[] visitorParticipations = epDTO.getEmployeeParticipation().getVisitorParticipations().toArray(new VisitorParticipation[0]);
185+
for (int j = 0; j < solvedConstraint.getTableIds().length; j++) {
184186
UUID tableKey = UUID.fromString(Arrays.copyOf(solvedConstraint.getTableIds(), solvedConstraint.getTableIds().length, String[].class)[0]);
185187

186188
UUID selectedChairId = tablesAndTheirSeats.get(tableKey).get(0);
187189
chairsToPersist.add(new Chair(selectedChairId, event));
188190

189191
// first the employee is seated
190192
if (j == 0) {
191-
employeeParticipationToChairMap.put(employeeParticipation.getId(), selectedChairId);
193+
employeeParticipationToChairMap.put(epDTO.getEmployeeParticipation().getId(), selectedChairId);
192194
tablesAndTheirSeats.get(tableKey).remove(0);
193195
// we also track employee's table assignment so that we can reference them back whn we calculate new entities for PreviousMatches table
194-
tableToEmployeesMap.computeIfAbsent(tableKey, k -> new ArrayList<>()).add(employeeParticipation.getEmployee().getId());
196+
tableToEmployeesMap.computeIfAbsent(tableKey, k -> new ArrayList<>()).add(epDTO.getEmployeeParticipation().getEmployee().getId());
195197
} else {
196198
visitorParticipationToChairMap.put(visitorParticipations[j - 1].getId(), selectedChairId);
197199
tablesAndTheirSeats.get(tableKey).remove(0);

0 commit comments

Comments
 (0)