The best I came up with is
public final T save(T containable) {
// if entity containable.getCompound already exists, it
// must first be reattached to the entity manager or else
// an exception will occur (issue in Spring Data JPA ->
// save() method internal calls persists instead of merge)
if (containable.getId() == null
&& containable.getCompound().getId() != null){
Compound compound = getCompoundService()
.getById(containable.getCompound().getId());
containable.setCompound(compound);
}
containable = getRepository().save(containable);
return containable;
}
We check if we are in the problematic situation and if yes just reload the existing entity from the database by its id and set the field of the new entity to this freshly loaded instance. It then will be attached.
This requires that the service for new entity holds a reference to the service of the referenced entity. This should not be an issue since you are using spring anyway so that service can just be added as a new @Autowired
field.
Another issue however (in my case this behavior is actually desired) that you can't change the referenced existing entity at the same time while saving the new one. All those changes will be ignored.
IMPORTANT NOTE:
In many and probably your cases this can be much simpler. You can add a reference of entity manager to your service:
@PersistenceContext
private EntityManager entityManager;
and in above if(){}
block use
containable = entityManager.merge(containable);
instead of my code (untested if it works).
In my case the classes are abstract and targetEntity
in @ManyToOne
is hence abstract too. Calling entityManager.merge(containable) directly then leads to an exception. However if your classes are all concrete this should work.