Hibernate.orgCommunity Documentation
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
>
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
.
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.
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.
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"/>
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.
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
>
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
.
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 9.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.