Visão geral dos tipos de cascata JPA/Hibernate

Visão geral dos tipos de cascata JPA / Hibernate

1. Introdução

Neste tutorial, discutiremos o que é cascateamento em JPA / Hibernate. Em seguida, cobriremos os vários tipos de cascata disponíveis, junto com sua semântica.

2. O que é cascata?

Os relacionamentos de entidades geralmente dependem da existência de outra entidade - por exemplo, o relacionamentoPerson -Address. Sem oPerson, a entidadeAddress não tem nenhum significado próprio. Quando excluímos a entidadePerson, nossa entidadeAddress também deve ser excluída.

Cascata é o caminho para conseguir isso. When we perform some action on the target entity, the same action will be applied to the associated entity.

2.1. Tipo de cascata JPA

Todas as operações em cascata específicas de JPA são representadas pela enumjavax.persistence.CascadeType contendo entradas:

  • ALL

  • PERSISTIR

  • MERGE

  • RETIRAR

  • ATUALIZAR

  • DETACH

2.2. Tipo de cascata de hibernação

O Hibernate suporta três tipos adicionais de cascata, juntamente com os especificados pela JPA. Esses tipos de cascata específicos do Hibernate estão disponíveis emorg.hibernate.annotations.CascadeType:

  • REPLICAR

  • SAVE_UPDATE

  • LOCK

3. Diferença entre os tipos de cascata

3.1. CascadeType.ALL

Cascade.ALL propaga todas as operações - incluindo aquelas específicas do Hibernate - de uma entidade pai para uma entidade filha.

Vamos ver em um exemplo:

@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
    private List
addresses; }

Observe que nas associaçõesOneToMany, mencionamos o tipo de cascata na anotação.

Agora, vamos ver a entidade associadaAddress:

@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String street;
    private int houseNumber;
    private String city;
    private int zipCode;
    @ManyToOne(fetch = FetchType.LAZY)
    private Person person;
}

3.2. CascadeType.PERSIST

A operação persistente torna uma instância transitória persistente. CascadeType PERSIST propagates the persist operation from a parent to a child entity. Quando salvamos a entidadeperson, a entidadeaddress também será salva.

Vamos ver o caso de teste para uma operação persist:

@Test
public void whenParentSavedThenChildSaved() {
    Person person = new Person();
    Address address = new Address();
    address.setPerson(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();
    session.clear();
}

Quando executarmos o caso de teste acima, veremos o seguinte SQL:

Hibernate: insert into Person (name, id) values (?, ?)
Hibernate: insert into Address (city, houseNumber, person_id, street, zipCode, id) values (?, ?, ?, ?, ?, ?)

3.3. CascadeType.MERGE

A operação de mesclagem copia o estado do objeto especificado no objeto persistente com o mesmo identificador. CascadeType.MERGE propagates the merge operation from a parent to a child entity.

Vamos testar a operação de fusão:

@Test
public void whenParentSavedThenMerged() {
    int addressId;
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();
    addressId = address.getId();
    session.clear();

    Address savedAddressEntity = session.find(Address.class, addressId);
    Person savedPersonEntity = savedAddressEntity.getPerson();
    savedPersonEntity.setName("devender kumar");
    savedAddressEntity.setHouseNumber(24);
    session.merge(savedPersonEntity);
    session.flush();
}

Quando executamos o caso de teste acima, a operação de mesclagem gera o seguinte SQL:

Hibernate: select address0_.id as id1_0_0_, address0_.city as city2_0_0_, address0_.houseNumber as houseNum3_0_0_, address0_.person_id as person_i6_0_0_, address0_.street as street4_0_0_, address0_.zipCode as zipCode5_0_0_ from Address address0_ where address0_.id=?
Hibernate: select person0_.id as id1_1_0_, person0_.name as name2_1_0_ from Person person0_ where person0_.id=?
Hibernate: update Address set city=?, houseNumber=?, person_id=?, street=?, zipCode=? where id=?
Hibernate: update Person set name=? where id=?

Aqui, podemos ver que a operação de mesclagem carrega primeiro as entidadesaddresseperson e depois as atualiza como resultado deCascadeType MERGE.

3.4. CascadeType.REMOVE

Como o nome sugere, a operação de remoção remove a linha correspondente à entidade do banco de dados e também do contexto persistente.

CascadeType.REMOVE propagates the remove operation from parent to child entity.Similar to JPA’s CascadeType.REMOVE, we have CascadeType.DELETE, which is specific to Hibernate. Não há diferença entre os dois.

Agora é hora de testarCascadeType.Remove:

@Test
public void whenParentRemovedThenChildRemoved() {
    int personId;
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();
    personId = person.getId();
    session.clear();

    Person savedPersonEntity = session.find(Person.class, personId);
    session.remove(savedPersonEntity);
    session.flush();
}

Quando executarmos o caso de teste acima, veremos o seguinte SQL:

Hibernate: delete from Address where id=?
Hibernate: delete from Person where id=?

Oaddress associado aperson também foi removido como resultado deCascadeType REMOVE.

3.5. CascadeType.DETACH

A operação de desanexação remove a entidade do contexto persistente. When we use CascaseType.DETACH, the child entity will also get removed from the persistent context.

Vamos ver em ação:

@Test
public void whenParentDetachedThenChildDetached() {
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();

    assertThat(session.contains(person)).isTrue();
    assertThat(session.contains(address)).isTrue();

    session.detach(person);
    assertThat(session.contains(person)).isFalse();
    assertThat(session.contains(address)).isFalse();
}

Aqui, podemos ver que após desanexarperson, nemperson nemaddress existe no contexto persistente.

3.6. CascadeType.LOCK

De forma não intuitiva,CascadeType.LOCK anexa novamente a entidade e sua entidade filha associada ao contexto persistente.

Vamos ver o caso de teste para entenderCascadeType.LOCK:

@Test
public void whenDetachedAndLockedThenBothReattached() {
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();

    assertThat(session.contains(person)).isTrue();
    assertThat(session.contains(address)).isTrue();

    session.detach(person);
    assertThat(session.contains(person)).isFalse();
    assertThat(session.contains(address)).isFalse();
    session.unwrap(Session.class)
      .buildLockRequest(new LockOptions(LockMode.NONE))
      .lock(person);

    assertThat(session.contains(person)).isTrue();
    assertThat(session.contains(address)).isTrue();
}

Como podemos ver, ao usarCascadeType.LOCK, anexamos a entidadeperson e seuaddress associado de volta ao contexto persistente.

3.7. CascadeType.REFRESH

Operações de atualizaçãore-read the value of a given instance from the database. Em alguns casos, podemos alterar uma instância após persistir no banco de dados, mas mais tarde precisamos desfazer essas alterações.

Nesse tipo de cenário, isso pode ser útil. When we use this operation with CascadeType REFRESH, the child entity also gets reloaded from the database whenever the parent entity is refreshed.

Para melhor compreensão, vamos ver um caso de teste paraCascadeType.REFRESH:

@Test
public void whenParentRefreshedThenChildRefreshed() {
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.persist(person);
    session.flush();
    person.setName("Devender Kumar");
    address.setHouseNumber(24);
    session.refresh(person);

    assertThat(person.getName()).isEqualTo("devender");
    assertThat(address.getHouseNumber()).isEqualTo(23);
}

Aqui, fizemos algumas alterações nas entidades salvaspersoneaddress. Quando atualizamos a entidadeperson,address também é atualizado.

3.8. CascadeType.REPLICATE

The replicate operation is used when we have more than one data source, and we want the data in sync. ComCascadeType.REPLICATE, uma operação de sincronização também se propaga para entidades filhas sempre que executada na entidade pai.

Agora, vamos testarCascadeType.REPLICATE:

@Test
public void whenParentReplicatedThenChildReplicated() {
    Person person = buildPerson("devender");
    person.setId(2);
    Address address = buildAddress(person);
    address.setId(2);
    person.setAddresses(Arrays.asList(address));
    session.unwrap(Session.class).replicate(person, ReplicationMode.OVERWRITE);
    session.flush();

    assertThat(person.getId()).isEqualTo(2);
    assertThat(address.getId()).isEqualTo(2);
}

Por causa deCascadeTypeREPLICATE, quando replicamos a entidadeperson, seuaddress associado também é replicado com o identificador que definimos.

3.9. CascadeType.SAVE_UPDATE

CascadeType.SAVE_UPDATE propaga a mesma operação para a entidade filha associada. É útil quando usamosHibernate-specific operations like save, update, and saveOrUpdate

Vamos verCascadeType.SAVE_UPDATE em ação:

@Test
public void whenParentSavedThenChildSaved() {
    Person person = buildPerson("devender");
    Address address = buildAddress(person);
    person.setAddresses(Arrays.asList(address));
    session.saveOrUpdate(person);
    session.flush();
}

Por causa deCascadeType.SAVE_UPDATE, quando executamos o caso de teste acima, podemos ver quepersoneaddress foram salvos. Aqui está o SQL resultante:

Hibernate: insert into Person (name, id) values (?, ?)
Hibernate: insert into Address (city, houseNumber, person_id, street, zipCode, id) values (?, ?, ?, ?, ?, ?)

4. Conclusão

Neste artigo, discutimos a cascata e as diferentes opções de tipos em cascata disponíveis no JPA e no Hibernate.

O código-fonte do artigo éavailable on GitHub.