引起:java.util.ConcurrentModificationException: null with Preremove annotation

时间:2020-12-30 10:49:12

标签: java spring-boot hibernate jpa

我有以下问题:

SUPPRESS

还有:

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

我知道问题出在哪里了。

我尝试更新一个子实体,即与父实体的 ManyToOne 关系。 当我更新孩子时,刷新不好。如果我改变父母,它会保留孩子。这不好。 因此,我正在尝试获取“旧”父级,从其列表中删除子级,然后继续更新。 但是当我删除孩子时,它会触发@Preremove 注释。 我不想那样。

这是孩子:

Caused by: java.util.ConcurrentModificationException: null

服务(@Service,@Transactional):

@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);
        }
    }

你有什么想法吗?

编辑 1:

删除作业:

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);
        }
....
}

编辑 2:

项目:

public Project removeJob(Job job) {
    this.jobs.remove(job);
    job.setProject(null);
    return this;
}

1 个答案:

答案 0 :(得分:1)

JPA - @PreRemove method behaviour 中所述,@PreRemove 是由移除孤儿作业触发的。

您正确同步了 Projet-Job 双向关联的两端,也许您应该避免使用 @PreRemove 执行其他双向关联同步,而是在 add* 中进行和 remove* 方法,而不是其他地方。

您不需要那么频繁地调用 repository.save。在事务方法中,对托管实体的更改会自动传播,因此您应该仅在非托管实体上使用它(此处,对于由 MapStruct 创建的 job 仅使用 1 次)。如果你真的需要冲洗,你可以使用repository.saveAndFlush方法,但乍一看这里没有必要。

顺便说一下,您保留了 JHipster 生成的代码,该代码使用相同的 save 方法创建和更新实体,该方法合并了 DTO 中的所有内容。我建议使用单独的创建和更新方法,以避免在其他问题中出现不需要的更新。 您还可以利用 MapStruct 进行更新: https://mapstruct.org/documentation/stable/reference/html/#updating-bean-instances

希望这会有所帮助!