I have the following service layer function in my Spring Boot + Spring Data JPA application:
@Service
public class MyService {
@Autowired
MyEntityRepository repository;
@Transactional
public void serviceMethod() {
MyEntity newEntity = new MyEntity("code", "name", "description");
newEntity = repository.save(newEntity); // --> all good here
// ...
newEntity.setName("updated name");
newEntity = repository.save(newEntity); // --> all good here
Set<ChildEntity> newChildrenEntities = Set.of(new ChildEntity("childName"));
newEntity.setChildren(newChildrenEntities);
newEntity = repository.save(newEntity); // --> Exception here!
// ...
}
}
And when the execution reaches the last save method, an Exception is raised:
java.lang.UnsupportedOperationException: null
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:73) ~[na:na]
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.clear(ImmutableCollections.java:79) ~[na:na]
at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:581) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.type.CollectionType.replace(CollectionType.java:757) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:167) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:451) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
...
If I switch the order of the operations, then the exception raises on the second method as well:
@Transactional
public void serviceMethod() {
MyEntity newEntity = new MyEntity("code", "name", "description");
Set<ChildEntity> newChildrenEntities = Set.of(new ChildEntity("childName"));
newEntity.setChildren(newChildrenEntities);
newEntity = repository.save(newEntity); // --> all good here
newEntity.setName("updated name");
newEntity = repository.save(newEntity); // --> Exception here!
}
However, if I change my newChildrenEntities implementation to a modifiable collection:
@Transactional
public void serviceMethod() {
MyEntity newEntity = new MyEntity("code", "name", "description");
newEntity = repository.save(newEntity); // --> all good here
// ...
newEntity.setName("updated name");
newEntity = repository.save(newEntity); // --> all good here
Set<ChildEntity> newChildrenEntities = new HashSet<>();
newChildrenEntities.add(new ChildEntity("childName"));
newEntity.setChildren(newChildrenEntities);
newEntity = repository.save(newEntity); // --> all good here!
// ...
}
Or if I remove the @Transactional annotation from my method, then everything works ok.
I'd like to know why exactly this happens, it seems related to how Hibernate handles collections.