6.1 Управление на дълбоки зависимости

И още някои полезни и интересни неща за @OneToMany анотации и други подобни. Всички те имат 4 често използвани опции:

  • cascade = CascadeType.ALL
  • orphanRemoval = вярно
  • fetch = FetchType.LAZY

Сега ще ги анализираме по-подробно. И ще започнем с най-интересното - CascadeType . Този параметър определя Howво трябва да се случи със зависимите обекти, ако променим основния обект.

Спецификацията на JPA има следните стойности за този параметър:

  • ВСИЧКО
  • УПОРСТВАМ
  • СЛИВАНЕ
  • ПРЕМАХВАНЕ
  • ОБНОВЯВАНЕ
  • ОТКАЧВАНЕ

Hibernate обаче разширява тази спецификация в още три опции:

  • РЕПЛИКАЦИЯ
  • SAVE_UPDATE
  • КЛЮЧАЛКА

Разбира се, има силен паралел с базата данни и техния CONSTRANIS. Има обаче и разлики. Hibernate се опитва да скрие реалната работа с базата данни, доколкото е възможно, така че тези Hibernate Cascades са точно за обекти Entity.

6.2 CascadeType

Параметърът cascade описва Howво трябва да се случи със зависимите обекти, ако променим техния родител (главен обект). Най-често този параметър се използва заедно с анотации, които описват зависимостите на обекта:

Пример:

OneToOne(cascade = CascadeType.ALL)

Или така:

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

Може да се напише и като отделна анотация:

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

Сега нека поговорим повече за това Howво означават тези анотации.

6.3 ALL, PERSIST, MERGE

CascadeType.ALLозначава, че всички действия , които извършваме с родителския обект, трябва да се повторят за неговите зависими обекти.

CascadeType.PERSISTозначава, че ако запазим родителския обект в базата данни, тогава същото трябва да се направи и с неговите зависими обекти. Пример:


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

Пример за работа с този клас:


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

Ние запазваме само обект от тип Employee, неговият зависим обект EmployeeTask ще бъде записан в базата данни автоматично.

CascadeType.MERGEозначава, че ако актуализираме родителския обект в базата данни, тогава същото трябва да се направи и с неговите зависими обекти.

6.4 ПРЕМАХВАНЕ, ИЗТРИВАНЕ, ОТКАЧВАНЕ

CascadeType.REMOVEозначава, че ако изтрием родителски обект в базата данни, тогава същото трябва да се направи с неговите зависими обекти.

CascadeType.DELETEозначава същото. Това са синоними. Просто от различни спецификации.

CascadeType.DETACHозначава, че ако премахнем родителския обект от сесията, тогава същото трябва да се направи и с неговите зависими обекти. Пример:


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

Пример за работа с този клас:


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.REFRESHи CascadeType.SAVE_UPDATEработят по същия начин, Howто очакваме - те дублират действията, които се извършват с родителския обект към неговия зависим обект.

6.5 Опция за премахване на осиротели

Също така понякога може да срещнете параметъра orphan. Това е съкращение от Орфан премахване. Използва се, за да се гарантира, че няма дъщерни обекти без родителски обекти.

OneToOne(orphan = true)

Ако този параметър е зададен на true, тогава дъщерният обект ще бъде изтрит, ако е изчезналвсички връзки. Не е точно същото като Cascade.REMOVE.

Може да имате ситуация, при която няколко родителски обекта се отнасят за едно дете. Тогава е полезно той да не се изтрива заедно с изтриването на родителския обект, а само ако всички препратки към него са анулирани.

Да приемем, че имате клас:


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

Обектът EmployeeTask ще бъде изтрит, защото няма останали препратки към него. В същото време никой не е изтрил родителския обект.

6.6 опция за извличане

Опцията за извличане ви позволява да контролирате How се зареждат зависимите обекти. Обикновено приема една от двете стойности:

  • FetchType.LAZY
  • FetchType.EAGER

Това е много интересна тема с различни подводни камъни, така че е по-добре да говоря за нея в отделна лекция.