6.1 Diep afhankelijkheidsbeheer

En nog wat meer nuttige en interessante dingen over @OneToMany- annotaties en dergelijke. Ze hebben allemaal 4 veelgebruikte opties:

  • cascade = CascadeType.ALL
  • weesVerwijdering = waar
  • fetch = FetchType.LAZY

Nu zullen we ze in meer detail analyseren. En we beginnen met de meest interessante - CascadeType . Deze parameter bepaalt wat er moet gebeuren met afhankelijke entiteiten als we de hoofdentiteit wijzigen.

De JPA-specificatie heeft de volgende waarden voor deze parameter:

  • ALLE
  • VOLHARDEN
  • SAMENVOEGEN
  • VERWIJDEREN
  • VERFRISSEN
  • ONTKOPPELEN

Hibernate breidt deze specificatie echter uit met nog drie opties:

  • REPLICEREN
  • OPSLAAN_UPDATE
  • SLOT

Er is natuurlijk een sterke parallel met de database en hun CONSTRANIS. Er zijn echter ook verschillen. Hibernate probeert het echte werk met de database zoveel mogelijk te verbergen, dus deze Hibernate Cascades gaan precies over Entity-objecten.

6.2 CascadeType

De cascadeparameter beschrijft wat er moet gebeuren met afhankelijke objecten als we hun ouder (hoofdobject) wijzigen. Meestal wordt deze parameter gebruikt in combinatie met annotaties die objectafhankelijkheden beschrijven:

Voorbeeld:

OneToOne(cascade = CascadeType.ALL)

Of zo:

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})

Het kan ook als een afzonderlijke annotatie worden geschreven:

@Cascade({ org.hibernate.annotations.CascadeType.ALL })

Laten we het nu hebben over wat deze annotaties betekenen.

6.3 ALLES, VOLHOUDEN, SAMENVOEGEN

CascadeType.ALLbetekent dat alle acties die we uitvoeren met het bovenliggende object moeten worden herhaald voor de afhankelijke objecten.

CascadeType.PERSISTbetekent dat als we het bovenliggende object in de database opslaan, hetzelfde moet gebeuren met de afhankelijke objecten. Voorbeeld:


@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @OneToOne(cascade = CascadeType.PERSIST, mappedBy="task")
   private EmployeeTask task;
}

Een voorbeeld van werken met deze klasse:


Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
 
session.persist(director);
session.flush();

We slaan alleen een object van het type Employee op, het afhankelijke object EmployeeTask wordt automatisch in de database opgeslagen.

CascadeType.MERGEbetekent dat als we het bovenliggende object in de database bijwerken, hetzelfde moet gebeuren met de afhankelijke objecten.

6.4 VERWIJDEREN, WISSEN, ONTKOPPELEN

CascadeType.REMOVEbetekent dat als we een bovenliggend object in de database verwijderen, hetzelfde moet gebeuren met de afhankelijke objecten.

CascadeType.DELETEbetekent hetzelfde. Dit zijn synoniemen. Alleen uit andere specificaties.

CascadeType.DETACHbetekent dat als we het bovenliggende object uit de sessie verwijderen, hetzelfde moet gebeuren met de afhankelijke objecten. Voorbeeld:


@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @OneToOne(cascade = CascadeType.DETACH, mappedBy="task")
   private EmployeeTask task;
}

Een voorbeeld van werken met deze klasse:


Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
director.task = task;
session.flush();
 
assertThat(session.contains(director)).isTrue();
assertThat(session.contains(task)).isTrue();
 
session.detach(director);
 
assertThat(session.contains(director)).isFalse();
assertThat(session.contains(task)).isFalse();

CascadeType.REFRESHen CascadeType.SAVE_UPDATEwerken op dezelfde manier als we verwachten - ze dupliceren de acties die worden uitgevoerd met het bovenliggende object naar het afhankelijke object.

6.5 Optie weeshuisverwijdering

Soms kom je ook de parameter tegen orphan. Dit is een afkorting voor weesverwijdering. Het wordt gebruikt om ervoor te zorgen dat er geen onderliggende entiteiten zijn zonder bovenliggende entiteiten.

OneToOne(orphan = true)

Als deze parameter is ingesteld op true, wordt de onderliggende entiteit verwijderd als deze is verdwenenalle koppelingen. Het is niet precies hetzelfde als Cascade.REMOVE.

U kunt een situatie hebben waarin meerdere bovenliggende entiteiten naar één kind verwijzen. Dan is het gunstig dat deze niet samen met het verwijderen van de bovenliggende entiteit wordt verwijderd, maar alleen als alle verwijzingen ernaar worden vernietigd.

Stel dat je een klas hebt:


@Entity
@Table(name="user")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @OneToMany(cascade = CascadeType.ALL, orphan = true)
   @JoinColumn(name = "employee_id")
   private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();
}


Employee director = session.find(Employee.class, 4);
EmployeeTask task = director.tasks.get(0);
director.tasks.remove(task)
session.persist(director);
session.flush();

Het object EmployeeTask wordt verwijderd omdat er geen verwijzingen meer naar zijn. Tegelijkertijd heeft niemand het bovenliggende object verwijderd.

6.6 ophaaloptie

Met de ophaaloptie kunt u bepalen hoe afhankelijke objecten worden geladen. Het duurt meestal een van de volgende twee waarden:

  • FetchType.LAZY
  • FetchType.EAGER

Dit is een heel interessant onderwerp met verschillende valkuilen, dus ik kan er maar beter over praten in een aparte lezing.