Mapeamento Herança Hibernate

22 respostas
M

Galera,

Tenho que mapear num sistema uma hierarquia de classes. Tenho a classe Acesso que é abstrata, e as classes AcessoPagina e AcessoComponente que estendem a classe abstrata.

Seguindo a documentação de referência do Hibernate, verifiquei a seguinte forma de realizar este mapeamento, criando uma tabela por classe concreta, ou seja, no meu BD exitem duas tabelas com os atributos da classe abstrata e das classes concretas.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="package.entity">
	<class name="Acesso" abstract="true" mutable="true">

		<cache usage="transactional" />

		<id name="id" type="long" column="ID_ACESSO">
			<generator class="native">
				<param name="sequence">SQ_ACESSO</param>
			</generator>
		</id>

		<property name="msisdn" type="string" column="MSISDN" />

		<union-subclass name="AcessoPagina" table="MD_ACESSO_PAGINA">
			<property name="idPagina" type="long" column="ID_PAGINA" />
			<property name="idAplicacao" type="long" column="ID_APLICACAO" /> 
			<many-to-one name="idPag" class="Pagina" column="ID_PAGINA" />
			<many-to-one name="idAplic" class="Aplicacao" column="ID_APLICACAO" />
		</union-subclass>

		<union-subclass name="AcessoComponente" table="MD_ACESSO_COMPONENTE">
			<property name="idComponente" type="long" column="ID_COMPONENTE" />
			<property name="idPagina" type="long" column="ID_PAGINA" />

			<many-to-one name="id" class="Componente" column="ID_COMPONENTE" />
			<many-to-one name="id" class="Pagina" column="ID_PAGINA" />
		</union-subclass>
	</class>
</hibernate-mapping>

Porém é apresentado o seguinte erro:

org.hibernate.MappingException: Repeated column in mapping for entity: package.entity.AcessoPagina column: ID_PAGINA (should be mapped with insert="false" update="false")

	at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:652)

	at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:674)

Alguém teria idéia da causa deste erro. Procurei por várias alternativas, mas sem sucesso até o momento :cry:

Abçs.

22 Respostas

A

Bom dia,

Você está declarando duas vezes o mesmo campo (ou coluna) da tabela.

<union-subclass name="AcessoPagina" table="MD_ACESSO_PAGINA"> <property name="idPagina" type="long" column="ID_PAGINA" /> //propriedade declarada pela primeira vez <property name="idAplicacao" type="long" column="ID_APLICACAO" /> <many-to-one name="idPag" class="Pagina" column="ID_PAGINA" /> //propriedade declarada pela segunda vez <many-to-one name="idAplic" class="Aplicacao" column="ID_APLICACAO" /> </union-subclass>

M

Olá AGAraujo,

Obrigado pela ajuda, mas fiz exatamente o que me indicou, e o erro continua o mesmo.

Alguma outra idéia?

Obrigado.

A

Passe por favor a estrurua das classes que resolvemos agora!!!
rs.

M

OK. Desde já agradeço a ajuda.

public abstract class Acesso implements Serializable {
	public Long id;
	public String msisdn;
	private Pagina pagina;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getMsisdn() {
		return msisdn;
	}

	public void setMsisdn(String msisdn) {
		this.msisdn = msisdn;
	}

	public Pagina getPagina() {
		return pagina;
	}

	public void setPagina(Pagina pagina) {
		this.pagina = pagina;
	}

	@Override
	public boolean equals(Object object) {
		if (!(object instanceof Acesso))
			return false;
		Acesso outroAcesso = (Acesso) object;
		return this.id == outroAcesso.id;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = 23 * hash + (getId() != null ? getId().hashCode() : 0);

		return hash;
	}
}
public class AcessoPagina extends Acesso {
	private static final long serialVersionUID = 1L;
	
	private Aplicacao aplicacao;

	public Aplicacao getAplicacao() {
		return aplicacao;
	}

	public void setAplicacao(Aplicacao aplicacao) {
		this.aplicacao = aplicacao;
	}

}
public class AcessoComponente extends Acesso {
	private static final long serialVersionUID = 1L;
	
	private Componente componente;

	public Componente getComponente() {
		return componente;
	}

	public void setComponente(Componente componente) {
		this.componente = componente;
	}
}

Meu mapeamento ATUAL:

<hibernate-mapping>
	<!-- Mapeamento da subclasse AcessoPagina -->
	<class name="package.AcessoPagina" table="MD_ACESSO_PAGINA" lazy="false">

		<cache usage="transactional" />

		<id name="id" type="long" column="ID_ACESSO">
			<generator class="native">
				<param name="sequence">SQ_ACESSO</param>
			</generator>
		</id>

		<property name="msisdn" type="string" column="MSISDN"
			not-null="true" />
		<!--  <property name="pagina" type="long" column="ID_PAGINA" /> -->
		<!--  <property name="aplicacao" type="long" column="ID_APLICACAO" /> -->

		<many-to-one name="pagina" class="Pagina">
			<column name="ID_PAGINA" />
		</many-to-one>

		<many-to-one name="aplicacao" class="Aplicacao">
			<column name="ID_APLICACAO" />
		</many-to-one>
	</class>

	<!-- Mapeamento da subclasse AcessoComponente -->
	<class name="package.AcessoComponente" table="MD_ACESSO_COMPONENTE"
		lazy="false">

		<cache usage="transactional" />

		<id name="id" type="long" column="ID_ACESSO">
			<generator class="native">
				<param name="sequence">SQ_ACESSO</param>
			</generator>
		</id>

		<property name="msisdn" type="string" column="MSISDN"
			not-null="true" />
		<!--  <property name="pagina" type="long" column="ID_PAGINA" /> -->
		<!--  <property name="aplicacao" type="long" column="ID_APLICACAO" /> -->

		<many-to-one name="componente" class="Componente">
			<column name="ID_COMPONENTE" />
		</many-to-one>

		<many-to-one name="pagina" class="Pagina">
			<column name="ID_PAGINA" /> 
		</many-to-one> 
	</class>
</hibernate-mapping>

Vlws :wink:

A

Boa cabra…

de cara já vi que o mapeamento anterior este estão diferentes… ou outro você mapeou como union-subclass e agora não.
O erro é o mesmo com este mapeamento?

De qualquer forma vou testar aqui e em 15 minutos retorno com a resposta.

A

Olha só consegui reproduzir seu erro fazendo como eu disse, utilizando a propriedade e o mapeamento de ManyToOne.

Voce num poderia mandar zipado apenas estas classes e seus respectivos mapeamentos para eu ver melhor, pois não consegui reproduzir o erro de outra forma.

Mande as 5 classes envolvidas e os respectivos mapeamentos. Ai resolvemos.

Vou preparar aqui o que fiz e envio ja tb

M

AgAraujo, obrigado pela atenção.

Seguinte, são estas as classes e apenas 1 arquivo de mapeamento. No caso ele mapaeia as classes concretas, e cada classe concreta possui uma tabela no BD.

Abçs.

M

AGAraujo:
Boa cabra…

de cara já vi que o mapeamento anterior este estão diferentes… ou outro você mapeou como union-subclass e agora não.
O erro é o mesmo com este mapeamento?

De qualquer forma vou testar aqui e em 15 minutos retorno com a resposta.

Verdade. Tentei de outra forma, mas ocorre o mesmo erro. :cry:

A

isso ocorre mesmo!! quanto o hibernate tem objetos que apontam para a mesma coluna vc tem que dizer qual que será persistido!! eu sempre deixo o objeto pro exemplo Pessoa como update false e insert false e crio um private Integer codPessoaPersistir;
desta forma quando eu quiser buscar a pessoa, sempre ela será carregada pelo mapeamento do objeto Pessoa e ao gravar ou atualizar seto a propriedade codPessoaPersistir

ok!!!

M

Olá arthurminarini,

Sim, mas reference-se a colunas diferentes, visto que no BD criei uma tabela para cada subclasse. Isso deveria ocorrer mesmo neste caso?

Abçs.

A

Cara seguinte, meu tempo está esgotando e queria deixar isto resolvido (em retribuíção ao que o GUJ já me ajudou). Vamos lá!!

Segue as classe que eu fiz o teste para tentar simular sua situação.

public abstract class Access implements Serializable{
    private Integer id;
    private String name;
    private Page page;
    ...
}

public class AccessComponent extends Access {
    ...
}

public class AccessPage extends Access {
    ...
}

public class Page implements Serializable{
    private Integer id;
    private String url;
    ...
}

Estas são as entidades que tenho e abaixo segue o mapeamento das classes utilizando a estratégia "table_per_class_concret".

Arquivo Access.hbm.xml
<hibernate-mapping>
    <class name="hibernateestudo.entities.Access" abstract="true">
           <id name="id" column="id" access="field" />
           <property name="name" column="name" />
           <union-subclass name="hibernateestudo.entities.AccessPage" table="tb_accesspage" >
               <many-to-one class="hibernateestudo.entities.Page" name="page" >
                    <column name="idpage" />
               </many-to-one>
           </union-subclass>
           <union-subclass name="hibernateestudo.entities.AccessComponent" table="tb_accesscomp" >
               <many-to-one class="hibernateestudo.entities.Page" name="page" >
                    <column name="idpage" />
               </many-to-one>
           </union-subclass>
    </class>
</hibernate-mapping>
Arquivo Page.hbm.xml
<hibernate-mapping>
  <class name="hibernateestudo.entities.Page" table="tb_page" >
       <id name="id" column="id" />
        <property name="url" column="url" />
  </class>
</hibernate-mapping>

Tente comparar com o que você tem. Este mapeamento cria 3 tabelas, sendo estas "TB_ACCESSPAGE", "TB_ACCESSCOMP" e "TB_PAGE".

Veja se ajuda ai.

A

arthurminarini:
isso ocorre mesmo!! quanto o hibernate tem objetos que apontam para a mesma coluna vc tem que dizer qual que será persistido!! eu sempre deixo o objeto pro exemplo Pessoa como update false e insert false e crio um private Integer codPessoaPersistir;
desta forma quando eu quiser buscar a pessoa, sempre ela será carregada pelo mapeamento do objeto Pessoa e ao gravar ou atualizar seto a propriedade codPessoaPersistir

ok!!!

Boa arthur,

só complementando sua colocação: o mapeamento para um propriedade mais de uma vez só deve ser visto em casos raros, sendo que na maioria das vezes é possível contornar isto. E claro que se isto realmente ocorrer é necessário definir no mapeamento que esta propriedade somente será alterada por uma única referência (no caso, as outras ficarão com updateble=false e insertable=false).

Porém para o caso do maranata o problema está na definição da estratégia de mapeamento com hierarquia (herança).

No primeiro caso que ele definiu, utilizou uma estratégia, depois postou outra. O caso que enviei a pouco para ele é a estratégia baseada na primeira situação, que resolve sem a preocupação de ter uma propriedade mapeada mais de uma vez.

M

Muito obrigado AGAraujo e arthurminarini.

AGAraujo vou tentar aqui, mas desde agradeço imensamente a atenção. Isso é raro. Parabéns!!!

Abçs.

M

Engradaço. Infelizmente não funcionou. Numa tentativa, quebrei a hierarquia, excluindo a classe Acesso e replicando seus atributos nas classes AcessoPagina e AcessoComponente (gerando duplicação de código :oops: ). Mas foi só para teste!!! E para minha surpresa gerou EXATAMENTE o mesmo erro…

Devo estar dando alguma outra cabeçada, mas qual?

A

Você já tem uma base de dados criada?
Está usando esta base de dados?
Se for manda o schema das 3 entidades (tabelas).

M

AGAraujo,

Tenho sim a base criada, a qual estou utilizando. As tabelas estão com suas respectivas FK criadas.

Segue os schemas solicitados. Mas eu criei uma tabela por subclasse. Para a classe abstrata eu não criei tabela, ok?

CREATE TABLE "MD_ACESSO_COMPONENTE" 
   (	"ID_ACESSO" NUMBER, 
	"ISDN" VARCHAR2(4000 BYTE), 
	"ID_COMPONENTE" NUMBER, 
	"ID_PAGINA" NUMBER
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS NOLOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS [telefone removido]
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "BDR" ;
CREATE TABLE "MD_ACESSO_PAGINA" 
   (	"ID_ACESSO" NUMBER, 
	"MSISDN" VARCHAR2(4000 BYTE), 
	"ID_PAGINA" NUMBER, 
	"ID_APLICACAO" NUMBER
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS NOLOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS [telefone removido]
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "BDR" ;

Abçs.

A

Cara fiz um teste usando sua entidades (tabelas)

Seu mapeamento deve ser este que se segue. Se ainda sim não der certo, o problema está em outro lugar… tipo: arquivo de configuração duplicado, ou coisas do tipo… mas teste primeiro.
Faça somente os ajustes necessários para suas classes.

<hibernate-mapping package="package.entity"> <class name="Acesso" abstract="true"> <cache usage="transactional" /> <id name="id" type="long" column="ID_ACESSO"> <generator class="native"> <param name="sequence">SQ_ACESSO</param> </generator> </id> <union-subclass name="AcessoPagina" table="MD_ACESSO_PAGINA"> <property name="isdn" type="string" column="ISDN" /> <many-to-one name="idPag" class="Pagina" column="ID_PAGINA" /> <many-to-one name="idAplic" class="Aplicacao" column="ID_APLICACAO" /> </union-subclass> <union-subclass name="AcessoComponente" table="MD_ACESSO_COMPONENTE"> <property name="msisdn" type="string" column="MSISDN" /> <many-to-one name="idComp" class="Componente" column="ID_COMPONENTE" /> <many-to-one name="idPag" class="Pagina" column="ID_PAGINA" /> </union-subclass> </class> </hibernate-mapping>

A

Estou reenviando o cogido adaptando as classes que me passou num post anterior.

Segue-se:

<hibernate-mapping package="package.entity">  
	<class name="Acesso" abstract="true">  
         <cache usage="transactional" />  
         <id name="id" type="long" column="ID_ACESSO">
             <generator class="native">  
                 <param name="sequence">SQ_ACESSO</param>  
             </generator>  
        </id>  
         <union-subclass name="AcessoPagina" table="MD_ACESSO_PAGINA">  
             <property name="msisdn" type="string" column="ISDN" />
             <many-to-one name="pagina" class="Pagina" column="ID_PAGINA" />  
             <many-to-one name="aplicacao" class="Aplicacao" column="ID_APLICACAO" />  
         </union-subclass>  
         <union-subclass name="AcessoComponente" table="MD_ACESSO_COMPONENTE">  
             <property name="msisdn" type="string" column="MSISDN" />
             <many-to-one name="componente" class="Componente" column="ID_COMPONENTE" />
             <many-to-one name="pagina" class="Pagina" column="ID_PAGINA" />
         </union-subclass>  
     </class>
</hibernate-mapping>

para as classes

public abstract class Acesso implements Serializable {  
    public Long id;  
    public String msisdn;  
    private Pagina pagina;  
    ...
} 

public class AcessoPagina extends Acesso {  
    private Aplicacao aplicacao;  
    ...
}

public class AcessoComponente extends Acesso {  
    private Componente componente;  
    ...
}

mantendo seu banco de dados como está.

M

Prezados,

O erro é bizarro. Primeiro limpei o cache do navegador e depois do tomcat (pasta work). Não resolveu. Depois, exclui as classes Acesso, AcessoComponente e AcessoPagina da aplicação. Exclui também o arquivo de mapeamento e também a referência no arquivo de configuração do hibernate.

Supreendemente, continua a ocorrer a maledito erro!!!

Bizarro :shock:

A

tente trocar para anotations tudo do zero novamente hehehe
sempre funcionou comigo usando anotations que vc pode usar um discriminador e se vc usa uma unica tabela ou não por classe
exemplo

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TIPO_DE_ATOR", discriminatorType=DiscriminatorType.STRING)
public abstract class Ator implements Serializable {
?
}
@Entity
@DiscriminatorValue(value="T")
public class Trabalhador extends Ator {
?
}
@Entity
@DiscriminatorValue(value="E")
public class Estudante extends Ator {
?
}
A

maranata:
Prezados,

O erro é bizarro. Primeiro limpei o cache do navegador e depois do tomcat (pasta work). Não resolveu. Depois, exclui as classes Acesso, AcessoComponente e AcessoPagina da aplicação. Exclui também o arquivo de mapeamento e também a referência no arquivo de configuração do hibernate.

Supreendemente, continua a ocorrer a maledito erro!!!

Bizarro :shock:

Brother, estou te enviando o projeto de teste de funcionamento. Pode modifica-loa vontade. Este projeto segue sua linha de raciocinio porém o banco de dados é criado em tempo de execução.
Uma outra sugestão é que você crie um projeto independente somente para seu teste sem CTRL+C e CTRL+V. O problema ai no seu é outro.
É aquele tipo de problema que “um virgula resolve atrapalhar”.

Depois posta ai qual o problema… cuidado para não mexer em coisa demais e testar e depois encontrar uma causa errada pois ai o problema pode voltar.

Outra coisa… só para divulgar uma experiência: uma vez um dos integrantes da minha equipe estava com alguns erros num projeto java do eclipse (o X vermelho) e ele tentava resolver um problema por vez, ou seja, o X vermelho continuava. Mesmo assim ele tentava realizar os testes e seja qual fosse a modificação que ele fazia não resolvia o problema e o erro continuava sempre o mesmo (por uns 2 dias). Até que eu mandei fazer um “rebuid” ai o projeto deu certo. Entendemos então que o Eclipse não recompila nada enquanto tiver o X vermelhinho. Assi, seja qual for sua modificação esta não será afetada. Estou vendo o mesmo caso na sua situação. Se encontrar alguma semelhança com isto tente da mesma maneira.

Segue o projeto para teste.

M

AGAraujo,

Cara muitíssimo obrigado. Prometo que posto a solução.

Abçs.

Criado 22 de novembro de 2010
Ultima resposta 23 de nov. de 2010
Respostas 22
Participantes 3