java - 原因:java.util.ConcurrentModificationException: null with Preremove annotation
问题描述
我有以下问题:
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
和:
Caused by: java.util.ConcurrentModificationException: null
我知道问题出在哪里。
我尝试更新子实体,与父实体的关系 ManyToOne。当我更新孩子时,刷新并不好。如果我改变父母,它会保留孩子。这不好。因此,我正在尝试获取“旧”父级,将子级从其列表中删除,然后继续更新。但是当我删除孩子时,它会触发 @Preremove 注释。我不希望那样。
这是孩子:
@Entity
@Table(name = "job", schema = "public")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Job implements Serializable {
...
...
@ManyToOne(cascade = CascadeType.REFRESH,fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonIgnoreProperties(value = "jobs", allowSetters = true)
private Project project;
..
...
...
public Project getProject() {
return project;
}
public Job project(Project project) {
this.project = project;
return this;
}
public void setProject(Project project) {
this.project = project;
}
...
...
...
@PreRemove
public void removeAppUsers() {
if(this.appUsers.size()>0)
for (AppUser ap : this.appUsers) {
ap.removeJob(this);
}
if(this.performances.size()>0)
for (Performance p : this.performances) {
p.setJob(null);
}
}
有服务(@Service,@Transactional):
public JobDTO save(JobDTO jobDTO) {
Optional<Project> projectToRemove;
Project p;
log.debug("Request to save Job : {}", jobDTO);
Job job = jobMapper.toEntity(jobDTO);
projectToRemove=projectRepository.findProjectByJob(jobDTO.getId());
jobRepository.save(job);
if(projectToRemove.isPresent()&&jobDTO.getProjectId()!=projectToRemove.get().getId()) {
projectToRemove.get().removeJob(job);
projectRepository.save(projectToRemove.get());
}
if (jobDTO.getProjectId() != null) {
p = projectRepository.findById(jobDTO.getProjectId()).get();
p.addJob(job);
projectRepository.save(p);
job.setProject(p);
jobRepository.save(job);
}
....
}
你有什么主意吗?
编辑1:
删除作业:
public Project removeJob(Job job) {
this.jobs.remove(job);
job.setProject(null);
return this;
}
编辑2:
项目:
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Job> jobs = new HashSet<>();
解决方案
正如JPA - @PreRemove method behavior中所解释的,@PreRemove 由孤立作业的删除触发。
您正在正确同步 Projet-Job 双向关联的两端,也许您应该避免使用@PreRemove
来执行其他双向关联同步,而是在add*
和remove*
方法中进行,而不是在其他地方进行。
你不需要repository.save
这么频繁地打电话。在事务方法中,对托管实体的更改会自动传播,因此您应该只在非托管实体上使用它(此处,job
MapStruct 仅为其创建 1 次)。如果真的需要刷机,可以使用repository.saveAndFlush
方法,但是这里乍一看没必要。
顺便说一句,您保留了 JHipster 生成的代码,该代码使用相同的save
方法创建和更新实体,该方法合并了 DTO 中的所有内容。我建议使用单独的创建和更新方法以避免不必要的更新以及其他问题。您还可以利用 MapStruct 进行更新:
https ://mapstruct.org/documentation/stable/reference/html/#updating-bean-instances
希望这可以帮助!
推荐阅读
- r - Choosing the right parameterization for latent profile analyses using the TidyLPA package
- elasticsearch - 与 Elastic Search 1.4.2 相比,Elastic Search 7.9 索引花费的时间太多
- entity-framework - 如何在 .NETCore 3.1 和 Blazor 中创建 DbContextFactory
- google-pay - CRYPTOGRAM_3DS - 为什么它不是唯一的选择?
- javascript - 是否可以在Javascript(Ionic,Angular)中将blob文件转换为base64Data?
- python - 悬停在元素上只工作一次,使用 move_to_element().perform()
- xaml - 我可以在 Xamarin 表单中获取控件的绑定路径吗?
- mongodb - 您如何将用户数据存储在一个或多个集合中?
- node.js - 我们可以在不使用 smpt 端口号主机名的情况下发送邮件吗
- azure - Azure AD 单点登录