Acho engraçado quando estamos começando a estudar Rails e alguém sempre fala que uma das vantagens é que o ActiveRecord do Rails tem suporte a relacionamento polimórfico. O que muita gente não sabe é que o hibernate também tem!
Antes de mais nada o que é um relacionamento polimórfico?
Seguindo o exemplo da apostila de rails da caelum imagine que eu tenho uma entidade Comentario que nada mais é do que a opinião de um usuário sobre qualquer coisa. Um restaurante, um prato, um chefe, etc. Como representamos isso no mundo OO?

Se não pensarmos no banco de dados, o problema já está resolvido. Criamos uma interface chamada Comentavel e basta fazer minhas entidades a implementarem que poderei relacioná-las com o Comentario. O meu Comentario TEM-UM [qualquer coisa que implemente Comentavel]
Uma das formas de resolver esse problema é anotar a interface com @Entity e usar o @Inheritance como se a interface fosse de fato uma classe mãe, mas essa é a maneira feia… A forma “correta” para esse caso é usar o @Any que não criará nenhuma tabela e terá o comportamento igual ao do ActiveRecord do Rails.
Para poder usar o @Any temos algumas outras anotações para “configurar” o relacionamento que são a @AnyMetaDef, @MetaValue e a provavelmente já conhecida @JoinColumn.
Exemplo completo:
@Entity
public class Comentario {
@Id
@GeneratedValue
private long id;
private String comentario;
@Any(metaColumn = @Column(name = "detail_type"))
@AnyMetaDef(idType = "long", metaType = "string", metaValues = {
@MetaValue(value = "RESTAURANTE", targetEntity = Restaurante.class),
@MetaValue(value = "PRATO", targetEntity = Prato.class) })
@JoinColumn(name = "detail_id")
private Comentavel comentavel;
...
}
Explicações:
O que o hibernate fará é criar 2 colunas novas na tabela, uma para o id do registro relacionado, como num Many-to-One comum, e uma segunda coluna para especificar de qual tabela que é aquele registro. Essa segunda coluna é necessária porque o relacionamento pode ser com qualquer tabela e apenas com o id seria impossível saber se o comentário é do Restaurante de id 1 ou do Prato de id 1.
A primeira coisa que é configurada é qual o tipo de dado do id. No nosso caso é o seguinte trecho de código que define @AnyMetaDef(idType = “long”
Lembrando que todas as entidades que vão se relacionar com o Comentario devem obrigatoriamente ter o mesmo tipo de dados no id. No meu exemplo, Long.
A segunda parte é configurar o que vai na segunda coluna, que é o restando do código:
@AnyMetaDef(idType = "long", metaType = "string", metaValues = {
@MetaValue(value = "RESTAURANTE", targetEntity = Restaurante.class),
@MetaValue(value = "PRATO", targetEntity = Prato.class) })
Aqui dizemos que o tipo de dado (“metaType”) dessa segunda coluna é String e quando fizer um relacionamento com um Restaurante ele colocará o valor “RESTAURANTE” nessa coluna e no caso de um Prato “PRATO”, ambos definidos com a anotação @MetaValue que dizemos qual o valor para cada Entidade.
Depois de inserir alguns registros teremos algo parecido com a tabela abaixo
| Id | Comentario | detail_id (O nome da coluna é defino no “name” do @JoinColumn) | detail_type (O nome é definido no “name” do @Column que é “metaColumn” do @Any) |
| 1 | Otimo restaurante | 1 | RESTAURANTE |
| 2 | Prato muito apimentado | 1 | PRATO |
| 3 | Faltou mais um acompanhamento | 4 | PRATO |
Também poderíamos ter especificado como metaType, por exemplo, long e com isso teríamos que configurar os meta values com números.
Exemplo:
@AnyMetaDef(idType = "long", metaType = "long", metaValues = {
@MetaValue(value = "1", targetEntity = Restaurante.class),
@MetaValue(value = "2", targetEntity = Prato.class) })
Com essa configuração a tabela gerada teria ficado assim
| Id | Comentario | detail_id | detail_type |
| 1 | Otimo restaurante | 1 | 1 |
| 2 | Prato muito apimentado | 1 | 2 |
| 3 | Faltou mais um acompanhamento | 4 | 2 |
Ainda sobre esse assunto, existe também a annotation @ManyToAny que merece ser estudada, mas esse post já ficou muito maior do que eu gostaria. Qual quer dúvida, como sempre, basta enviar comentário ou email.
Muito legal este post!!!, eu fiz uma vc isso na mão!!!, a tabela ficou igualzinha… não sabia dessa feature do hibernate. Valeu Paniz!!
Muito bom cabelo, vai ajudar muito.
David, estou desenvolvendo um sistema em uma linha de produto em uma disciplina do mestrado. Estamos usando Flex + Cairngorm + BlazeDS + Java + Spring + Hibernate. Como estamos desenvolvendo baseado em componentes nos encontramos com a seguinte duvida.
Temos a classe Submission do Componente Submission e uma classe Event no Componente Event. Temos também um componente Core que se liga a cada componente. Mas temos um relacionamento entre as classes Submission e Event, pois em submission temos um atributo do tipo Event.
Para quebrar o acoplamento entre vários componentes e um possível relacionamento cíclico decidimos colocar uma interfaçe IEvent no Core e fazer com que a classe Event do Componente Event implemente esta interface. Assim a classe Submission passa a ter um atributo do tipo IEvent event (como o componente Sbmission e Event estão ligados ao core, quebraríamos o acoplamento direto entre os componentes Submission e Event).
O problema é como seria a maneira certa de fazer um mapeamento no hibernate utilizando um inteface. no nosso caso mapear o atibuto event da classe Submission. Tentamos fazer o que foi proposto e em outras fontes, mas a seguinte exception aparece:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in ServletContext resource [/WEB-INF/config/database.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @OneToOne or @ManyToOne on br.com.rise.risechair.submission.model.Submission.IEvent references an unknown entity: br.com.rise.risechair.core.api.IEvent
Alguma sugestão, o que poderia ser????
se for possível fornecer o seu email eu posso enviar o código referente para vc olhar…
Bom dia Jonatas,
Esse erro está acontecendo porque você está tentando usar o @OneToOne ou @ManyToOne pra uma classe (Interface no caso) que não está anotada com @Entity. Pra resolver esse problema você tem 2 opções: Ou usa o @Entity na interface e cria o schema como se tivesse usando herança mesmo (Primeiro exemplo desse post usando @Inheritance); Ou substitui a anotação no Submission pra @Any ou @ManyToAny (Lembrando que pra esse caso tem todas as configurações de meta que estão nesse post). Pra qualquer outra dúvida pode entrar em contato pelo email contato@davidpaniz.com
Boa tarde,
Achei muito bom este post, ele foi muito útil para mim. Mas agora estou tendo um problema com consultas orientadas a objetos.
Criei uma interface e duas classes para implementar a mesma. Gostaria de saber como fazer um select orientado a objetos da coluna definida em
“@Any(metaColumn = @Column(name = “tipo”))”
Estou utilizando um NamedQuery
Se tiver uma forma de fazer isso seria ótimo pois não quero utilizar sql nativa.
Grato desde já,
David
Oi David,
na hql funciona normalmente utilizar o campo polimórfico. Seguindo o exemplo do post você poderia fazer, por exemplo, a seguinte query:
“select c Comentario c where c.comentavel = :pComantavel”
Muito bom, resolveu o meu problema! Valeu!!! Abraco