hibernate更新到OneToMany集合的唯一约束违规

时间:2017-09-22 17:43:15

标签: java hibernate hibernate-onetomany

我有以下实体:

@Entity
public class License  {

     // ...

    @OneToMany(mappedBy = "license", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    private Set<Service> services = new HashSet<>();

    // ...

    protected void setServices(Set<String> services) {
        this.services.clear();
        if (services != null) {
            this.services.addAll(services.stream().map(s -> new Service(this, s)).collect(toSet()));
        }
    }
}

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "UQ_lic_service", columnNames = { "license_id", "service" }))
public class Service {

    // ...

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "license_id", nullable = false)
    private License license;

    @Column(nullable = false, length = 255)
    private String service;

    protected Service(License license, String service) {
        this.license = license;
        this.service = service;
    }
}

现在我需要更新License,如:

    License license = getSomeExistingLicenseWithServices(...);
    // The existing license has two services "firstService", "secondService"
    HashSet<String> services = new HashSet<>();
    services.add("firstService");
    license.setServices(services);
    licenseRepository.save(license); // A spring-data-jpa-repository

运行此命令,我得到以下异常:

Caused by: org.h2.jdbc.JdbcSQLException: Eindeutiger Index oder Primärschlüssel verletzt: "UQ_LIC_SERVICE_INDEX_8 ON PUBLIC.SERVICE(LICENSE_ID, SERVICE) VALUES (1, 'firstService', 1)"
Unique index or primary key violation: "UQ_LIC_SERVICE_INDEX_8 ON PUBLIC.SERVICE(LICENSE_ID, SERVICE) VALUES (1, 'firstService', 1)"; SQL statement:
insert into service (id, lock_no, license_id, service) values (null, ?, ?, ?) [23505-196]

我期待所有的老人&#39;当我使用新Service致电setServices时,(=&#39;孤立的&#39;)HashSet将被删除。我做错了什么? TIA!

2 个答案:

答案 0 :(得分:4)

在集合上调用.clear()可能还不够。可能它需要在清除它之前在集合的每个组件上显式调用remove()。

请在清除设置之前尝试添加类似的内容以查看是否能解决问题

this.services.forEach(service -> {licenseRepository.remove(service)});

答案 1 :(得分:2)

在设置服务之前,尝试清除列表

license.getServices().clear();

然后

license.getServices.add("firstService");
licenseRepository.save(license);

更新

为了实现这一目标,实施equalshashCode方法非常重要。我正在使用apache commons lang library

@Override
public int hashCode() {
    return new HashCodeBuilder().append(idService).toHashCode();
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Service))
        return false;

    Service other = (Service) obj;

    return new EqualsBuilder()
            .append(this.idService, other.idService)
            .isEquals();
}