JPA SecondaryTable Mapping

Olá pessoal,

Tenho o seguinte problema: possuo duas tabelas em um banco de dados legado e preciso mapeá-las para uma única classe Java, mas estou obtendo um erro. As tabelas são as seguintes:


   create table tb_servidor (  
     cd_servidor number primary key,  
     cd_tipo number,  
     ...  
     cd_nome number,  
     FOREIGN KEY(cd_nome) REFERENCES tb_nome(cd_nome)   
   )   
     
   create table tb_nome(  
     cd_nome number primary key,  
     nm_nome varchar2(32000)  
   )  

A classe Java que implementei é a seguinte:


   package entity;  
   import ...  
   @Entity  
   @Table(name = "TB_SERVIDOR")  
   @SecondaryTable(name = "TB_NOME",   
           pkJoinColumns = { @PrimaryKeyJoinColumn(name = "CD_NOME", referencedColumnName = "CD_NOME") })  
   public class Juiz{  
     
       @Column(name = "cd_servidor", nullable = false)  
       @Id  
       private int codigo;  
   //Se eu tirar esse campo e a anotação SecondaryTable, tudo funciona, mas não faz o que eu quero.  
   //Ademais já tentei usar um ManyToOne ou OneToOne aqui, mas também não funcionou.  
       @Column(table="TB_NOME", name = "NM_NOME")  
       private String nome;  
     
       public int getCodigo() {  
           return codigo;  
       }  
     
       public void setCodigo(int codigo) {  
           this.codigo = codigo;  
       }  
     
       public String getNome() {  
           return nome;  
       }  
     
       public void setNome(String nome) {  
           this.nome = nome;  
       }  
     
       @Override  
       public int hashCode() {  
           final int prime = 31;  
           int result = 1;  
          result = prime * result + codigo;  
           return result;  
       }  
     
       @Override  
       public boolean equals(Object obj) {  
           if (this == obj)  
               return true;  
           if (obj == null)  
               return false;  
           if (getClass() != obj.getClass())  
               return false;  
           final Juiz other = (Juiz) obj;  
           if (codigo != other.codigo)  
               return false;  
           return true;  
       }  
     
   }

O código JUnit que testa a classe acima é


    package entity;  
    import ...  
    public class JuizTest {  
      
        private EntityManagerFactory emf;  
        private EntityManager em;  
      
        @Before  
        public void initEmfAndEm() {  
           Logger.getLogger("org").setLevel(Level.ERROR);  
     
           emf = Persistence  
                   .createEntityManagerFactory("jurisprudenciaPersistenceUnit");  
           em = emf.createEntityManager();  
     
       }  
     
       @After  
       public void cleanup() {  
           if (em != null)  
               em.close();  
       }  
     
       @SuppressWarnings("unchecked")  
       @Test  
       public void insertAndRetrieve() {  
           em.getTransaction().begin();  
     
           List<Juiz> list = em.createQuery(  
                   "select DISTINCT p from Juiz p where p.codigo=74")  
                   .getResultList();  
           assertEquals(1, list.size());  
           for (Juiz c : list) {  
               System.out.println(c);  
           }  
     
       }  
     
   }  

O erro encontrado é o seguinte


    javax.persistence.PersistenceException: org.hibernate.MappingException: Unable to find column with logical name: CD_NOME in org.hibernate.mapping.Table(TB_SERVIDOR) and its related supertables and secondary tables  
        at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:258)  
        at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:120)  
        at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:37)  
        at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:27)  
        at entity.JuizTest.initEmfAndEm(JuizTest.java:28)  
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)  
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)  
        at java.lang.reflect.Method.invoke(Unknown Source)  
        at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:122)  
        at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:86)  
        at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)  
        at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)  
        at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)  
        at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)  
        at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)  
        at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)  
        at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)  
        at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)  
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)  
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)  
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)  
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)  
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)  
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)  
    Caused by: org.hibernate.MappingException: Unable to find column with logical name: CD_NOME in org.hibernate.mapping.Table(TB_SERVIDOR) and its related supertables and secondary tables  
        at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:394)  
        at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:231)  
        at org.hibernate.cfg.annotations.EntityBinder.bindJoinToPersistentClass(EntityBinder.java:519)  
        at org.hibernate.cfg.annotations.EntityBinder.createPrimaryColumnsToSecondaryTable(EntityBinder.java:509)  
        at org.hibernate.cfg.annotations.EntityBinder.finalSecondaryTableBinding(EntityBinder.java:440)  
        at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:770)  
        at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:498)  
        at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:277)  
        at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1115)  
        at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1269)  
        at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:150)  
        at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:888)  
        at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:186)  
        at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:246)  
        ... 24 more 

Eu estou achando que o problema porque cd_nome no chave primria na tabela tb_servidor, mas apenas foreign key, e que, para @SecondaryTable, a coluna referenciada precisa fazer parte da chave primria em ambas as tabelas. Se o problema for esse no sei nem que annotation usar para resolver a questo ou, ainda, se JPA possui soluo para tal problema.

Olá pessoal,

Encontrei uma solução temporária, mas que ainda está com cara de gambiarra: usei a annotation @Formula. Contudo, gerei um outro problema, como posso estender a classe para suportar updates, inserts e deletes? Se alguém encontrar uma solução melhor do que essa, sinta-se à vontade para apresentá-la.

package entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Formula;

@Entity
@Table(name = "tb_servidor")
public class Juiz {

	@Column(name = "cd_servidor", nullable = false)
	@Id
	private int codigo;
	@Column(updatable = false, insertable = false)  
	@Formula("(select n.nm_nome from tb_nome n join tb_servidor s on s.cd_nome = n.cd_nome where s.cd_servidor = cd_servidor)")  
	private String nome;
	public Juiz(){
		
	}

	/**
	 * @return the codigo
	 */
	public int getCodigo() {
		return codigo;
	}

	/**
	 * @param codigo
	 *            the codigo to set
	 */
	public void setCodigo(int codigo) {
		this.codigo = codigo;
	}

	/**
	 * @return the nome
	 */
	public String getNome() {
		return nome;
	}

	/**
	 * @param nome
	 *            the nome to set
	 */
	public void setNome(String nome) {
		this.nome = nome;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + codigo;
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Juiz other = (Juiz) obj;
		if (codigo != other.codigo)
			return false;
		return true;
	}
	@Override
	public String toString() {
		return getNome()+" ("+getCodigo()+")";
	}
}

Olá pessoal,

Encontrei uma solução temporária, mas que ainda está com cara de gambiarra: usei a annotation @Formula. Contudo, gerei um outro problema, como posso estender a classe para suportar updates, inserts e deletes? Se alguém encontrar uma solução melhor do que essa, sinta-se à vontade para apresentá-la.

package entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Formula;

@Entity
@Table(name = "tb_servidor")
public class Juiz {

	@Column(name = "cd_servidor", nullable = false)
	@Id
	private int codigo;
	@Column(updatable = false, insertable = false)  
	@Formula("(select n.nm_nome from tb_nome n join tb_servidor s on s.cd_nome = n.cd_nome where s.cd_servidor = cd_servidor)")  
	private String nome;
	public Juiz(){
		
	}

	/**
	 * @return the codigo
	 */
	public int getCodigo() {
		return codigo;
	}

	/**
	 * @param codigo
	 *            the codigo to set
	 */
	public void setCodigo(int codigo) {
		this.codigo = codigo;
	}

	/**
	 * @return the nome
	 */
	public String getNome() {
		return nome;
	}

	/**
	 * @param nome
	 *            the nome to set
	 */
	public void setNome(String nome) {
		this.nome = nome;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + codigo;
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Juiz other = (Juiz) obj;
		if (codigo != other.codigo)
			return false;
		return true;
	}
	@Override
	public String toString() {
		return getNome()+" ("+getCodigo()+")";
	}
}

up