Reputation: 15
I have several Items that are linked together in ManyToMany relations like this:
In Technologies:
@ManyToMany(mappedBy = "internalTechnologies")
private List<Project> internalProjects = new ArrayList<>();
In Projects
@ManyToMany(cascade = {CascadeType.MERGE})
@JoinTable(
name = "project_technology",
joinColumns = @JoinColumn(name = "project_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "technology_id", referencedColumnName = "id")
)
private List<Technology> internalTechnologies = new ArrayList<>();
When editing the technology to add or remove the links between the technology and projects, I use helper classes that use the @Service notion and are @Autowired into the controller.
For removing the links:
public void removeTechnologyFromProjects(Technology technologyToRemove, List<Project> projects){
List<Project> projectsToEdit = new ArrayList<>(projects);
for ( Project project: projectsToEdit) {
List<Technology> internalTechnologies = project.getInternalTechnologies();
internalTechnologies.remove(technologyToRemove);
project.setInternalTechnologies(internalTechnologies);
projectRepository.save(project);
}
}
Note: I create a new ArrayList to avoid ConcurrentModificationException.
For adding Links:
public void linkTechnologyToProjects(Technology technologyToAdd, List<Long> ids) throws ResponseStatusException {
for (Long id : ids) {
Optional<Project> project = projectRepository.findById(id);
if (project.isPresent()) {
Project projectToEdit = project.get();
List<Technology> technologyList = projectToEdit.getInternalTechnologies();
technologyList.add(technologyToAdd);
projectToEdit.setInternalTechnologies(technologyList);
projectRepository.save(projectToEdit);
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
String.format(PROJECT_NOT_FOUND, id));
}
}
}
In the Technology controller the following commands are executed:
@PutMapping("/tech/{id}")
public TechReturnDTO updateTech(@PathVariable("id") long id, @Valid @RequestBody TechCreationDTO requestBody) {
Technology techToUpdate = techRep.findById(id);
(other fields edited)
if (requestBody.getInternalProjects()!=null){
linkRemover.removeTechnologyFromProjects(techToUpdate, techToUpdate.getInternalProjects());
itemLinker.linkTechnologyToProjects(techToUpdate, requestBody.getInternalProjects());
}else{
linkRemover.removeTechnologyFromProjects(techToUpdate, techToUpdate.getInternalProjects());
}
(other lists edited)
Technology savedTech = techRep.save(techToUpdate);
return new TechReturnDTO(savedTech);
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
String.format("Technology with Id %s not found!", id));
}
For some reason, the method return the TechReturnDTO before the itemLinker does its job:
How do I 'force' the method to wait until the service has finished?
Edit: Additional details as requested:
The technologyRepository simply is an extension of the CrudRepository Class:
public interface TechnologyRepository extends CrudRepository<Technology, Long> { Technology findById(long id); }
It does return a fully Updated Object normally. When editing a Project the changes are immediately shown. Only when editing the tech to update the relation the error occurs.
Upvotes: 0
Views: 170
Reputation: 15
For who ever reads this: I did solve the issue by not solving it:
Instead of using the "mappedBy" of the bi-directional relationship, I changed the relationship to be two one-directional manyToMany relations.
I then changed the Services to update the corresponding items to contain the link.
Upvotes: 1