java - 无法使用 Spring Data JPA / Hibernate 在事务中两次保存具有不可变集合的实体
问题描述
我的 Spring Boot + Spring Data JPA 应用程序中有以下服务层功能:
@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!
// ...
}
}
当执行到最后一个save
方法时,会引发异常:
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]
...
如果我切换操作的顺序,那么第二种方法也会引发异常:
@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!
}
但是,如果我将newChildrenEntities
实现更改为可修改的集合:
@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!
// ...
}
或者,如果我从我的方法中删除@Transactional
注释,那么一切正常。
我想知道为什么会发生这种情况,这似乎与 Hibernate 如何处理集合有关。
解决方案
由于 Hibernate 在管理刷新/刷新/加载时需要控制/更改实体的状态,因此您会在自动刷新期间看到此异常。我建议您不要尝试将不可变集合用于实体映射。相反,您可以确保只向 API 用户公开不可变版本,但 Hibernate 可能需要更改内容。如果您认为这是一个错误或可以改进以支持您的用例,您还可以在问题跟踪器(https://hibernate.atlassian.net)中使用测试用例( https://github.com )创建问题/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java)重现该问题。
推荐阅读
- docker - SSH 代理转发到 docker-compose 文件中的 docker 容器
- javascript - 如何在其他文件中调用函数的getter
- python - Python如何使用for循环对齐文本?
- python - 如何在 Python 中生成具有 4 位字符串和 4 位数字的随机码?
- php - rest api codeigniter 结果缺失列
- python - 考虑每个计数重新计算平均值
- python - 如何将base64字符串作为图像保存到django python中的本地服务器
- wordpress - 覆盖 global/wrapper-start.php 不起作用
- reactjs - 接收排版文件的尝试导入错误
- sql-server - SQL,有没有办法遍历一列中的所有行然后计算值,然后遍历另一列中的所有行并计算无联合