Reputation: 3329
I have the following entities:
@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;
}
}
Now I need to update the License
like:
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
Running this, I get the following exception:
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]
I expected all 'old' (='orphaned') Service
s to become deleted, when I call setServices
with a new HashSet
. What I am doing wrong? TIA!
Upvotes: 1
Views: 3612
Reputation: 2221
Try to clear the list before setting the services:
license.getServices().clear();
And then
license.getServices.add("firstService");
licenseRepository.save(license);
UPDATE
To make this work, it is important to implement the equals
and hashCode
methods. I'm using 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();
}
Upvotes: 2
Reputation: 41
Just calling .clear() on the set may not be enough. Likely its requiring an explicit call to remove() on each component of the set before clearing it.
please try adding something like this before clearing the set to see if that solves the issue
this.services.forEach(service -> {licenseRepository.remove(service)});
Upvotes: 4