休眠更新到 OneToMany 集合的唯一约束冲突

Posted

技术标签:

【中文标题】休眠更新到 OneToMany 集合的唯一约束冲突【英文标题】:Unique constraint violation on hibernate update to OneToMany collection 【发布时间】:2017-09-22 17:43:14 【问题描述】:

我有以下实体:

@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]

当我用新的HashSet 调用setServices 时,我预计所有“旧”(=“孤立”)Services 都会被删除。我做错了什么?蒂亚!

【问题讨论】:

你让它工作了吗? 【参考方案1】:

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

请在清除集合之前尝试添加类似的内容,看看是否能解决问题

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

【讨论】:

谢谢。但我不能这样做。我需要一些级联解决方案。【参考方案2】:

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

license.getServices().clear();

然后

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

更新

要完成这项工作,实现equalshashCode 方法很重要。我正在使用 apache commons lang 库

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

【讨论】:

在添加单独查找此docs.jboss.org/hibernate/orm/5.2/userguide/html_single/…之前不太清楚

以上是关于休眠更新到 OneToMany 集合的唯一约束冲突的主要内容,如果未能解决你的问题,请参考以下文章

休眠:覆盖(实体)超类的 OneToMany 映射字段?

如果插入的值与唯一约束冲突,则 SQL 触发器在插入时更新字段

注释 ConcurrentHashMap 时,在休眠中“非法尝试将非集合映射为 @OneToMany、@ManyToMany 或 @CollectionOfElements”

无法在休眠中删除 OneToMany 关系的实例

删除元素时使用 JoinTable 和 OrderColumn 的 Hibernate 单向 OneToMany 映射中的约束冲突

冲突部分索引的 Postgres 唯一或排除约束无法更新票证