6.1 Gerenciamento de dependência profunda

E algumas coisas mais úteis e interessantes sobre as anotações @OneToMany e afins. Todos eles têm 4 opções comumente usadas:

  • cascata = CascadeType.ALL
  • orphanRemoval = verdadeiro
  • buscar = FetchType.LAZY

Agora vamos analisá-los com mais detalhes. E vamos começar com o mais interessante - CascadeType . Este parâmetro determina o que deve acontecer com as entidades dependentes se mudarmos a entidade principal.

A especificação JPA possui os seguintes valores para este parâmetro:

  • TODOS
  • PERSISTIR
  • MERGE
  • REMOVER
  • ATUALIZAR
  • SEPARAR

No entanto, o Hibernate expande essa especificação em mais três opções:

  • REPLICAR
  • SAVE_UPDATE
  • TRANCAR

Existe, claro, um forte paralelo com o banco de dados e seus CONSTRANIS. No entanto, também existem diferenças. O Hibernate tenta esconder o máximo possível o trabalho real com o banco de dados, então essas cascatas do Hibernate são exatamente sobre objetos Entity.

6.2 Tipo Cascata

O parâmetro cascade descreve o que deve acontecer com os objetos dependentes se mudarmos seu pai (objeto mestre). Na maioria das vezes, esse parâmetro é usado junto com anotações que descrevem as dependências do objeto:

Exemplo:

OneToOne(cascade = CascadeType.ALL)

Ou assim:

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

Também pode ser escrito como uma anotação separada:

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

Agora vamos falar mais sobre o que essas anotações significam.

6.3 TUDO, PERSISTIR, MERGE

CascadeType.ALLsignifica que todas as ações que executamos com o objeto pai devem ser repetidas para seus objetos dependentes.

CascadeType.PERSISTsignifica que se salvarmos o objeto pai no banco de dados, o mesmo deve ser feito com seus objetos dependentes. Exemplo:


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

Um exemplo de trabalho com esta classe:


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

Salvamos apenas um objeto do tipo Employee, seu objeto dependente EmployeeTask será salvo no banco de dados automaticamente.

CascadeType.MERGEsignifica que se atualizarmos o objeto pai no banco de dados, o mesmo deve ser feito com seus objetos dependentes.

6.4 REMOVER, EXCLUIR, SEPARAR

CascadeType.REMOVEsignifica que se excluirmos um objeto pai no banco de dados, o mesmo deve ser feito com seus objetos dependentes.

CascadeType.DELETEsignifica o mesmo. Estes são sinônimos. Apenas de especificações diferentes.

CascadeType.DETACHsignifica que se removermos o objeto pai da sessão, o mesmo deve ser feito com seus objetos dependentes. Exemplo:


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

Um exemplo de trabalho com esta classe:


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.REFRESHe CascadeType.SAVE_UPDATEfuncionam da mesma forma que esperamos - eles duplicam as ações que são executadas com o objeto pai para seu objeto dependente.

6.5 Opção de remoção órfã

Além disso, às vezes você pode encontrar o parâmetro orphan. Isso é a abreviação de remoção de órfãos. Ele é usado para garantir que não haja entidades filhas sem entidades pai.

OneToOne(orphan = true)

Se este parâmetro for definido como verdadeiro, a entidade filha será excluída se tiver desaparecidotodos os links. Não é exatamente o mesmo que Cascade.REMOVE.

Você pode ter uma situação em que várias entidades pai se referem a um filho. Então é benéfico que não seja excluído junto com a exclusão da entidade pai, mas apenas se todas as referências a ela forem anuladas.

Digamos que você tenha uma classe:


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

O objeto EmployeeTask será excluído porque não há mais referências a ele. Ao mesmo tempo, ninguém excluiu o objeto pai.

6.6 opção de busca

A opção de busca permite controlar como os objetos dependentes são carregados. Geralmente, assume um dos dois valores:

  • FetchType.LAZY
  • FetchType.EAGER

Este é um tópico muito interessante com várias armadilhas, então é melhor falar sobre isso em uma palestra separada.