Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 24. Exemplo: Pai/Filho

Uma das primeiras coisas que um usuário tenta fazer com o Hibernate é modelar um tipo de relacionamento Pai/Filho. Existem duas abordagens diferentes para isto. Por diversas razões diferentes, a abordagem mais conveniente, especialmente para novos usuários, é modelar ambos os `Parent` e `Child` como classes de entidade com uma associação `<one-to-many>` a partir do `Parent` para o `Child`. A abordagem alternativa é declarar o `Child` como um `<composite-element>`. As semânticas padrões da associação um para muitos (no Hibernate), são muito menos parecidas com as semânticas comuns de um relacionamento pai/filho do que aqueles de um mapeamento de elemento de composição. Explicaremos como utilizar uma associação bidirecional um para muitos com cascatas para modelar um relacionamento pai/filho de forma eficiente e elegante.

## 24.1. Uma nota sobre as coleções

As coleções do Hibernate são consideradas uma parte lógica de suas próprias entidades, nunca das entidades contidas. Saiba que esta é uma distinção que possui as seguintes conseqüências:

- Quando removemos ou adicionamos um objeto da/na coleção, o número da versão do proprietário da coleção é incrementado.

- Se um objeto removido de uma coleção for uma instância de um tipo de valor (ex.: um elemento de composição), este objeto irá parar de ser persistente e seu estado será completamente removido do banco de dados. Da mesma forma, ao adicionar uma instância de tipo de valor à coleção, causará ao estado uma persistência imediata.

- Por outro lado, se uma entidade é removida de uma coleção (uma associação um-para-muitos ou muitos-para-muitos), ela não será deletada por padrão. Este comportamento é completamente consistente, uma mudança para o estado interno de uma outra entidade não deve fazer com que a entidade associada desapareça. Da mesma forma, ao adicionar uma entidade à coleção, não faz com que a entidade se torne persistente, por padrão.

A adição de uma entidade à coleção, por padrão, meramente cria um link entre as duas entidades. A remoção da entidade, removerá o link. Isto é muito apropriado para alguns tipos de casos. No entanto, não é apropriado o caso de um relacionamento pai/filho. Neste caso, a vida do filho está vinculada ao ciclo de vida do pai.

## 24.2. Bidirecional um-para-muitos

Suponha que começamos uma associação `<one-to-many>` simples de `Parent` para `Child`.

    <set name="children">

        <key column="parent_id"/>

        <one-to-many class="Child"/>

    </set

    >

Se fossemos executar o seguinte código:

    Parent p = .....;

    Child c = new Child();

    p.getChildren().add(c);

    session.save(c);

    session.flush();

O Hibernate editaria duas instruções SQL

- um `INSERT` para criar um registro para `c`

- um `UPDATE` para criar um link de `p` para `c`

Isto não é somente ineficiente como também viola qualquer restrição `NOT NULL` na coluna `parent_id`. Nós podemos concertar a violação da restrição de nulabilidade, especificando um `not-null="true"` no mapeamento da coleção:

    <set name="children">

        <key column="parent_id" not-null="true"/>

        <one-to-many class="Child"/>

    </set

    >

No entanto, esta não é uma solução recomendada.

As causas subjacentes deste comportamento é que o link (a chave exterior `parent_id`) de `p` para `c` não é considerada parte do estado do objeto `Child` e por isso não é criada no `INSERT`. Então a solução é fazer uma parte de link do mapeamento do `Child`.

    <many-to-one name="parent" column="parent_id" not-null="true"/>

Nós também precisamos adicionar a propriedade `parent` à classe do `Child`.

Agora que a entidade `Child` está gerenciando o estado do link, informaremos à coleção para não atualizar o link. Utilizamos o atributo `inverse` para isto:

    <set name="children" inverse="true">

        <key column="parent_id"/>

        <one-to-many class="Child"/>

    </set

    >

O seguinte código seria usado para adicionar um novo `Child`:

    Parent p = (Parent) session.load(Parent.class, pid);

    Child c = new Child();

    c.setParent(p);

    p.getChildren().add(c);

    session.save(c);

    session.flush();

E agora, somente um SQL `INSERT` seria editado.

Para assegurar tudo isto, podemos criar um método de `addChild()` do `Parent`.

    public void addChild(Child c) {

        c.setParent(this);

        children.add(c);

    }

Agora, o código que adiciona um `Child` se parece com este:

    Parent p = (Parent) session.load(Parent.class, pid);

    Child c = new Child();

    p.addChild(c);

    session.save(c);

    session.flush();

## 24.3. Ciclo de vida em Cascata

A chamada explícita para `save()` ainda é incômoda. Iremos nos referir a ela utilizando cascatas.

    <set name="children" inverse="true" cascade="all">

        <key column="parent_id"/>

        <one-to-many class="Child"/>

    </set

    >

Isto simplifica o código acima para:

    Parent p = (Parent) session.load(Parent.class, pid);

    Child c = new Child();

    p.addChild(c);

    session.flush();

Da mesma forma, não precisamos repetir este comando com os filhos ao salvar ou deletar um `Parent`. O comando seguinte irá remover o `p` e todos os seus filhos do banco de dados.

    Parent p = (Parent) session.load(Parent.class, pid);

    session.delete(p);

    session.flush();

No entanto, este código:

    Parent p = (Parent) session.load(Parent.class, pid);

    Child c = (Child) p.getChildren().iterator().next();

    p.getChildren().remove(c);

    c.setParent(null);

    session.flush();

não irá remover `c` do banco de dados. Neste caso, ele somente removerá o link para `p` e causará uma violação de restrição `NOT NULL`). Você precisará `delete()` de forma explícita o `Child`.

    Parent p = (Parent) session.load(Parent.class, pid);

    Child c = (Child) p.getChildren().iterator().next();

    p.getChildren().remove(c);

    session.delete(c);

    session.flush();

Agora, no seu caso, um `Child` não pode existir sem seu pai. Então, se removermos um `Child` da coleção, não iremos mais querer que ele seja deletado. Devido a isto, devemos utilizar um `cascade="all-delete-orphan"`.

    <set name="children" inverse="true" cascade="all-delete-orphan">

        <key column="parent_id"/>

        <one-to-many class="Child"/>

    </set

    >

Apesar do mapeamento da coleção especificar `inverse="true"`, as cascatas ainda são processadas por repetição dos elementos de coleção. Portanto, se você requiser que um objeto seja salvo, deletado ou atualizado por uma cascata, você deverá adicioná-lo à sua coleção. Chamar `setParent()` não é o bastante.

## 24.4. Cascatas e `unsaved-value`

Suppose we loaded up a `Parent` in one `Session`, made some changes in a UI action and wanted to persist these changes in a new session by calling `update()`. The `Parent` will contain a collection of children and, since the cascading update is enabled, Hibernate needs to know which children are newly instantiated and which represent existing rows in the database. We will also assume that both `Parent` and `Child` have generated identifier properties of type `Long`. Hibernate will use the identifier and version/timestamp property value to determine which of the children are new. (See Seção 11.7, “Detecção automática de estado”.) In Hibernate3, it is no longer necessary to specify an `unsaved-value` explicitly.

O seguinte código atualizará o `parent` e o `child` e inserirá um `newChild`:

    //parent and child were both loaded in a previous session

    parent.addChild(child);

    Child newChild = new Child();

    parent.addChild(newChild);

    session.update(parent);

    session.flush();

Bem, isto cabe bem no caso de um identificador gerado, mas e os identificadores atribuídos e os identificadores de composição? Isto é mais difícil, pois uma vez que o Hibernate não pode utilizar a propriedade do identificador para distinguir entre um objeto instanciado recentemente, com um identificador atribuído pelo usuário, e um objeto carregado em uma sessão anterior. Neste caso, o Hibernate usará tanto um carimbo de data e hora (timestamp) ou uma propriedade de versão, ou irá na verdade consultar um cache de segundo nível, ou no pior dos casos, o banco de dados, para ver se a linha existe.

## 24.5. Conclusão

Há muito o que digerir aqui e pode parecer confuso na primeira vez. No entanto, na prática, funciona muito bem. A maioria dos aplicativos do Hibernate utiliza o modelo pai/filho em muitos lugares.

Nós mencionamos uma alternativa neste primeiro parágrafo. Nenhum dos casos acima existem no caso de mapeamentos `<composite-element>`, que possuem exatamente a semântica do relacionamento pai/filho. Infelizmente, existem duas grandes limitações para elementos compostos: elementos compostos podem não possuir coleções e assim sendo podem não ser filhos de nenhuma outra entidade a não ser do pai único.

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 23. Additional modules
- Acima
- Principal
- PróximaCapítulo 25. Exemplo: Aplicativo Weblog