Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 10. Mapeamento de Herança

## 10.1. As três estratégias

O Hibernate suporta as três estratégias básicas de mapeamento de herança:

- tabela por hierarquia de classes

- table per subclass

- tabela por classe concreta

Além disso, o Hibernate suporta um quarto tipo de polimorfismo um pouco diferente:

- polimorfismo implícito

É possível usar diferentes estratégias de mapeamento para diferentes ramificações da mesma hierarquia de herança. Você pode fazer uso do polimorfismo implícito para alcançá-lo através da hierarquia completa. De qualquer forma, o Hibernate não suporta a mistura de mapeamentos `<subclass>`, `<joined-subclass>` e `<union-subclass>` dentro do mesmo elemento raíz `<class>`. É possível usar, junto às estratégias, uma tabela por hierarquia e tabela por subclasse, abaixo do mesmo elemento `<class>`, combinando os elementos `<subclass>` e `<join>` (veja abaixo).

É possível definir mapeamentos `subclass`, `union-subclass` e `joined-subclass` em documentos de mapeamento separados, diretamente abaixo de `hibernate-mapping`. Isso permite que você estenda uma hierarquia de classes apenas adicionando um novo arquivo de mapeamento. Você deve especificar uma função `extends` no mapeamento da subclasse, nomeando uma superclasse previamente mapeada. Anteriormente esta característica fazia o ordenamento dos documentos de mapeamento importantes. Desde o Hibernate3, o ordenamento dos arquivos de mapeamento não importa quando usamos a palavra chave extends. O ordenamento dentro de um arquivo de mapeamento simples ainda necessita ser definido como superclasse antes de subclasse.

     <hibernate-mapping>

         <subclass name="DomesticCat" extends="Cat" discriminator-value="D">

              <property name="name" type="string"/>

         </subclass>

     </hibernate-mapping

    >

### 10.1.1. Tabela por hierarquia de classes

Vamos supor que temos uma interface `Payment`, com sua implementação `CreditCardPayment`, `CashPayment` e `ChequePayment`. O mapeamento da tabela por hierarquia seria parecido com:

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            ...

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            ...

        </subclass>

    </class

    >

É requisitado exatamente uma tabela. Existe uma grande limitação desta estratégia de mapeamento: colunas declaradas por subclasses, tais como `CCTYPE`, podem não ter restrições `NOT NULL`.

### 10.1.2. Tabela por subclasse

Um mapeamento de tabela por subclasse seria parecido com:

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="AMOUNT"/>

        ...

        <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </joined-subclass>

        <joined-subclass name="CashPayment" table="CASH_PAYMENT">

            <key column="PAYMENT_ID"/>

            ...

        </joined-subclass>

        <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            <key column="PAYMENT_ID"/>

            ...

        </joined-subclass>

    </class

    >

São necessárias quatro tabelas. As três tabelas subclasses possuem associação de chave primária para a tabela de superclasse, desta maneira o modelo relacional é atualmente uma associação de um-para-um.

### 10.1.3. Tabela por subclasse: usando um discriminador

A implementação de tabela por subclasse do Hibernate não necessita de coluna de discriminador. Outro mapeador objeto/relacional usa uma implementação diferente de tabela por subclasse, que necessita uma coluna com o tipo discriminador na tabela da superclasse. A abordagem escolhida pelo Hibernate é muito mais difícil de implementar, porém mais correto de um ponto de vista relacional. Se você deseja utilizar uma coluna discriminadora com a estratégia tabela por subclasse, você poderá combinar o uso de `<subclass>` e `<join>`, dessa maneira:

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <join table="CREDIT_PAYMENT">

                <key column="PAYMENT_ID"/>

                <property name="creditCardType" column="CCTYPE"/>

                ...

            </join>

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            <join table="CASH_PAYMENT">

                <key column="PAYMENT_ID"/>

                ...

            </join>

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            <join table="CHEQUE_PAYMENT" fetch="select">

                <key column="PAYMENT_ID"/>

                ...

            </join>

        </subclass>

    </class

    >

A declaração opcional `fetch="select"` diz ao Hibernate para não buscar os dados da subclasse `ChequePayment`, quando usar uma união externa pesquisando a superclasse.

### 10.1.4. Mesclar tabela por hierarquia de classes com tabela por subclasse

Você pode até mesmo mesclar a estratégia de tabela por hierarquia e tabela por subclasse usando esta abordagem:

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <join table="CREDIT_PAYMENT">

                <property name="creditCardType" column="CCTYPE"/>

                ...

            </join>

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            ...

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            ...

        </subclass>

    </class

    >

Para qualquer uma dessas estratégias de mapeamento, uma associação polimórfica para a classe raíz `Payment` deve ser mapeada usando `<many-to-one>`.

    <many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

### 10.1.5. Tabela por classe concreta

Existem duas formas que poderíamos usar a respeito da estratégia de mapeamento de tabela por classe concreta. A primeira é usar `<union-subclass>`.

    <class name="Payment">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="sequence"/>

        </id>

        <property name="amount" column="AMOUNT"/>

        ...

        <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </union-subclass>

        <union-subclass name="CashPayment" table="CASH_PAYMENT">

            ...

        </union-subclass>

        <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            ...

        </union-subclass>

    </class

    >

Três tabelas estão envolvidas para as subclasses. Cada tabela define colunas para todas as propriedades da classe, incluindo propriedades herdadas.

A limitação dessa abordagem é que se uma propriedade é mapeada na superclasse, o nome da coluna deve ser o mesmo em todas as tabelas das subclasses. A estratégia do gerador identidade não é permitida na união da herança de sub-classe. A fonte de chave primária deve ser compartilhada através de todas subclasses unidas da hierarquia.

Se sua superclasse é abstrata, mapeie-a com `abstract="true"`. Claro, que se ela não for abstrata, uma tabela adicional (padrão para `PAYMENT` no exemplo acima), será necessária para segurar as instâncias da superclasse.

### 10.1.6. Tabela por classe concreta usando polimorfismo implícito

Uma abordagem alternativa é fazer uso de polimorfismo implícito:

    <class name="CreditCardPayment" table="CREDIT_PAYMENT">

        <id name="id" type="long" column="CREDIT_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CREDIT_AMOUNT"/>

        ...

    </class>

    <class name="CashPayment" table="CASH_PAYMENT">

        <id name="id" type="long" column="CASH_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CASH_AMOUNT"/>

        ...

    </class>

    <class name="ChequePayment" table="CHEQUE_PAYMENT">

        <id name="id" type="long" column="CHEQUE_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CHEQUE_AMOUNT"/>

        ...

    </class

    >

Veja que em nenhum lugar mencionamos a interface `Payment` explicitamente. Note também que propriedades de `Payment` são mapeadas em cada uma das subclasses. Se você quiser evitar duplicação, considere usar entidades de XML (ex. `[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]` na declaração do `DOCTYPE` e `& allproperties;` no mapeamento).

A desvantagem dessa abordagem é que o Hibernate não gera `UNION`s de SQL quando executa pesquisas polimórficas.

Para essa estratégia, uma associação polimórfica para `Payment` geralmente é mapeada usando `<any>`.

    <any name="payment" meta-type="string" id-type="long">

        <meta-value value="CREDIT" class="CreditCardPayment"/>

        <meta-value value="CASH" class="CashPayment"/>

        <meta-value value="CHEQUE" class="ChequePayment"/>

        <column name="PAYMENT_CLASS"/>

        <column name="PAYMENT_ID"/>

    </any

    >

### 10.1.7. Mesclando polimorfismo implícito com outros mapeamentos de herança

Existe ainda um item a ser observado sobre este mapeamento. Como as subclasses são mapeadas em seu próprio elemento `<class>`, e como o `Payment` é apenas uma interface, cada uma das subclasses pode ser facilmente parte de uma outra hierarquia de herança! (E você ainda pode usar pesquisas polimórficas em cima da interface `Payment`.)

    <class name="CreditCardPayment" table="CREDIT_PAYMENT">

        <id name="id" type="long" column="CREDIT_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="CREDIT_CARD" type="string"/>

        <property name="amount" column="CREDIT_AMOUNT"/>

        ...

        <subclass name="MasterCardPayment" discriminator-value="MDC"/>

        <subclass name="VisaPayment" discriminator-value="VISA"/>

    </class>

    <class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">

        <id name="id" type="long" column="TXN_ID">

            <generator class="native"/>

        </id>

        ...

        <joined-subclass name="CashPayment" table="CASH_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="amount" column="CASH_AMOUNT"/>

            ...

        </joined-subclass>

        <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="amount" column="CHEQUE_AMOUNT"/>

            ...

        </joined-subclass>

    </class

    >

Mais uma vez, nós não mencionamos `Payment` explicitamente. Se nós executarmos uma pesquisa em cima da interface `Payment`, por exemplo, `from Payment` – o Hibernate retorna automaticamente instâncias de `CreditCardPayment` (e suas subclasses, desde que elas também implementem `Payment`), `CashPayment` e `ChequePayment` mas não as instâncias de `NonelectronicTransaction`.

## 10.2. Limitações

Existem certas limitações para a abordagem do "polimorfismo implícito" comparada com a estratégia de mapeamento da tabela por classe concreta. Existe uma limitação um tanto menos restritiva para mapeamentos `<union-subclass>`.

A seguinte tabela demonstra as limitações do mapeamento de tabela por classe concreta e do polimorfismo implícito no Hibernate.

Tabela 10.1. Recurso dos Mapeamentos de Herança

Estratégia de Herança | muitos-para-um Polimórfico | um-para-um Polimórfico | um-para-muitos Polimórfico | muitos-para-um Polimórfico | Polimórfico `load()/get()` | Consultas Polimórficas | Junçöes Polimórficas | Busca por união externa
--- | --- | --- | --- | --- | --- | --- | --- | ---
tabela por hierarquia de class | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
table per subclass | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
tabela por classe concreta (subclasses de união) | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` (for `inverse="true"` only) | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
tabela por classe concreta (polimorfismo implícito) | `<any>` | not supported | not supported | `<many-to-any>` | `s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()` | `from Payment p` | not supported | not supported

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 9. Mapeamento de Componentes
- Acima
- Principal
- PróximaCapítulo 11. Trabalhando com objetos