Hibernate multiple saves required for orphan removal

102 views Asked by At

Consider the following entity model:

The dossier entity.

@Entity
@Table(name = "dossiers", uniqueConstraints = @UniqueConstraint(columnNames = { "site_id", "dossier_id" }))
@Audited(withModifiedFlag = true)
@Access(AccessType.FIELD)
public final class Dossier extends EntityObject {

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

    @OneToOne(fetch = FetchType.EAGER, mappedBy = "dossier", optional = true, cascade = CascadeType.ALL, orphanRemoval = true)
private Commission commission;

}

The commissions entity.

@Entity
@Table(name = "commissions")
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@Access(AccessType.FIELD)
public class Commission extends EntityObject{

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "commission", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @OrderBy(clause = "boundary ASC NULLS LAST")
    private List<CommissionBracket> commissionBrackets;

    @OneToOne(optional = true)
    @JoinColumn(name = "dossier_id", nullable = true)
    private Dossier dossier;

}

The bracket entity.

@Entity
@Table(name = "commission_brackets")
@Access(AccessType.FIELD)
public class CommissionBracket extends EntityObject {

    @ManyToOne(optional = false)
    @JoinColumn(name = "commission_id")
    private Commission commission;

    @Column(name = "boundary")
    private Integer boundary;
}

Now when I want to replace the commission from a dossier. I have to do this:

    boolean notSaved = true;
    if(domain.getCommission() != null) {
        toSave.setCommission(domain.getCommission());
        toSave.getCommission().getCommissionBrackets().clear();
        toSave = dossierRepository.save(toSave);
        toSave.setCommission(null);
        toSave = dossierRepository.save(toSave);
        notSaved = false;
    }
    if(dossier.getCommission() != null) {
        final Commission newCommission = commissionRepository.save(dossier.getCommission());
        toSave.setCommission(newCommission);
        toSave = dossierRepository.save(toSave);
        notSaved = false;
    }
    if(notSaved) {
        toSave = dossierRepository.save(toSave);
    }

Note that the save operation on a repository does a persist or merge depending on whether the object passed has its id set or not.

While a normal person with a sane mind would think that he could simply do:

dossier.setCommission(newCommission);
dossierRepository.save(newCommission);

For some reason, regardless if there are brackets attached to the commission or not I need to:

  1. Clear the brackets on the existing commission
  2. Save
  3. Remove the existing commission
  4. Save
  5. Persist the new commission
  6. Set the new commission on the Dossier
  7. Save

Skipping any of the steps causes this error:

A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: Commission.commissionBrackets

While there might be a plausible explanation for this it feels very ugly and maybe there is a cleaner way to do it? Instead of removing the existing commission I could also update it entirely but that would not reflect the functional reality.

Hibernate version used is: 4.3.10.Final DB used: postgres 9.4.4.1

So my questions are. Am I doing it right or it be improved somewhere?

1

There are 1 answers

4
Predrag Maric On

You didn't post all relevant entity details, like Commission's relation to Dossier, but mappedBy says it is there. Since Dossier is in a bidirectional relation to Commission (and Commission happens to be the owning side of the relation), you just need to maintain both sides. This should work:

// break the current relation, should cause old commissions object to be deleted
dossier.getCommission().setDossier(null);
newCommission.setDossier(dossier);
dossier.setCommission(newCommission);
dossierRepository.save(dossier);