So, I have this scenario where I need to take a header record, delete the details for it, then re-create the details in a different way. Updating the details would be way too much trouble.
I basically have:
@Transactional
public void create(Integer id, List<Integer> customerIDs) {
Header header = headerService.findOne(id);
// header is found, has multiple details
// Remove the details
for(Detail detail : header.getDetails()) {
header.getDetails().remove(detail);
}
// Iterate through list of ID's and create Detail with other objects
for(Integer id : customerIDs) {
Customer customer = customerService.findOne(id);
Detail detail = new Detail();
detail.setCustomer(customer);
header.getDetails().add(detail);
}
headerService.save(header);
}
Now, the database has a constraint like the following:
Header
=================================
ID, other columns...
Detail
=================================
ID, HEADER_ID, CUSTOMER_ID
Customer
=================================
ID, other columns...
Constraint: Details must be unique by HEADER_ID and CUSTOMER_ID so:
Detail (VALID)
=================================
1, 123, 10
2, 123, 12
Detail (IN-VALID)
=================================
1, 123, 10
1, 123, 10
OK, when I run this and pass in 2, 3, 20, etc. customers, it creates all Detail
records just fine as long as there weren't any before.
If I run it again, passing in a different list of customers, I expect ALL
details to be deleted first then a list of NEW
details to be created.
But what's happening is that the delete doesn't seem to be honored before the create. Because the error is a duplicate key constraint. The duplicate key is the "IN-VALID" scenario above.
If I manually populate the database with a bunch of details and comment out the CREATE details
part (only run the delete) then the records are deleted just fine. So the delete works. The create works. It's just that both don't work together.
I can provide more code is needed. I'm using Spring Data JPA
.
Thanks
UPDATE
My entities are annotated with basically the following:
@Entity
@Table
public class Header {
...
@OneToMany(mappedBy = "header", orphanRemoval = true, cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Set<Detail> Details = new HashSet<>();
...
}
@Entity
@Table
public class Detail {
...
@ManyToOne(optional = false)
@JoinColumn(name = "HEADER_ID", referencedColumnName = "ID", nullable = false)
private Header header;
...
}
UPDATE 2
@Klaus Groenbaek
Actually, I didn't mention this originally but I did it that way the first time. Also, I am using Cascading.ALL which I assume includes PERSIST.
Just for testing, I have updated my code to the following:
@Transactional
public void create(Integer id, List<Integer> customerIDs) {
Header header = headerService.findOne(id);
// Remove the details
detailRepository.delete(header.getDetails()); // Does not work
// I've also tried this:
for(Detail detail : header.getDetails()) {
detailRepository.delete(detail);
}
// Iterate through list of ID's and create Detail with other objects
for(Integer id : customerIDs) {
Customer customer = customerService.findOne(id);
Detail detail = new Detail();
detail.setCustomer(customer);
detail.setHeader(header);
detailRepository.save(detail)
}
}
Again...I want to reiterate....that the delete WILL WORK if I don't have the create immediately afterwards. The create WILL WORK if I don't have the delete immediately before it. But neither will work if they are together because of the duplicate key constraint error from the database.
I've tried the same scenario WITH and WITHOUT cascading deletes.