JPA - Mapear campo que é pk e fk ao mesmo tempo (Resolvido)

Oi Pessoa!

Tenho no meu modelo uma classe PessoaUsuario que entre outros campos é composta por uma objeto do tipo Pessoa e ambas possuem suas próprias tabelas no banco de dados. No caso na base de dados a tabela PessoaUsuario possue um campo codigo que é ao mesmo tempo pk e fk vindo da tabela pessoa. Então no modelo de classes eu poderia usar algo do tipo “pessoaUsuario.getPessoa().getId()” ou “pessoaUsuario.getPessoa().getEmail()”.

A dificuldade está sendo em mapear isso usando JPA. Sempre caio em lugares falando sobre chave composta, utilizando as anotações @EmbbedId ou @IdClass, mas nesse caso não é uma chave composta e sim um campo que é ao mesmo tempo pk e fk numa relação de um para um.

Tem alguma idéia?

O detalhe é que o banco é legado e não posso simplesmente criar um campo id que não é uma fk lá…

To tem ums três dias pesquisando e ainda não achei nada sobre isso…

Uma coisa curiosa. Se eu crio a tabela PessoaUsuario na mão, colocando apenas a anotação @Id em cima do meu objeto Pessoa que fica dentro de PessoaUsuario, o jpa cria na minha tabela um campo blob chamado Pessoa…

Me parece ser uma coisa tão comum… vou continuar tentando aqui.

Oi renatocustodio,

Você está fazendo exatamente como pede o tutorial ou algum livro sobre o assunto? Porque isso que vc descreveu é considerado uma das associações (um para um) entre entidades mais simples de mapear.

O que acontece quando vc executa? É apresentado algum erro?

 Talvez se vc postar o modelo das duas tabelas e as suas classes correspondentes a estas tabelas anime o pessoal a te ajudar.

[]'s

[quote=renatocustodio]Oi Pessoa!

Tenho no meu modelo uma classe PessoaUsuario que entre outros campos é composta por uma objeto do tipo Pessoa e ambas possuem suas próprias tabelas no banco de dados. No caso na base de dados a tabela PessoaUsuario possue um campo codigo que é ao mesmo tempo pk e fk vindo da tabela pessoa. Então no modelo de classes eu poderia usar algo do tipo “pessoaUsuario.getPessoa().getId()” ou “pessoaUsuario.getPessoa().getEmail()”.

A dificuldade está sendo em mapear isso usando JPA. Sempre caio em lugares falando sobre chave composta, utilizando as anotações @EmbbedId ou @IdClass, mas nesse caso não é uma chave composta e sim um campo que é ao mesmo tempo pk e fk numa relação de um para um.

Tem alguma idéia?

O detalhe é que o banco é legado e não posso simplesmente criar um campo id que não é uma fk lá…[/quote]

Eu faço isso usando herança (uma tabela por classe):

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Pessoa implements Serializable {

	public Pessoa() {
	}
private Integer idPessoa;

	/**
	 * Getter of the property <tt>id</tt>
	 *
	 * @return Returns the id.
	 * 
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	public Integer getIdPessoa()
	{
		return idPessoa;
	}

	/**
	 * Setter of the property <tt>id</tt>
	 *
	 * @param id The id to set.
	 *
	 */
	public void setIdPessoa(Integer idPessoa){
		this.idPessoa = idPessoa;
	}
}

public class Usuario extends Pessoa {

	public Usuario() {
	}
}

Boa noite

Cara eu li mas não consegui entender bem ao certo o teu problema. Teria como você postar o teu MER pra eu dar uma olhada. Dias atras passei por vários problemas de mapeamento e demorei também pra resolve-los mas no final consegui achar a solução.

As vezes eu posso te ajudar: Só pra constar o meu mapeamento é esse:

TABLE: assistema

id_sistema pk
nome

TABLE: asmodulo
id_modulo pk
nome

TABLE assitema_modulo

sistema pk e fk
modulo pk e fk

O meu problema foi efetuar esse mapeamento e também fazer o mapeamento das tabelas que estavam referenciando a sistema_modulo. Se o teu problema for parecido acho que posso te ajudar.

Abracos.

[quote=fantomas]Oi renatocustodio,

Você está fazendo exatamente como pede o tutorial ou algum livro sobre o assunto? Porque isso que vc descreveu é considerado uma das associações (um para um) entre entidades mais simples de mapear.

O que acontece quando vc executa? É apresentado algum erro?

 Talvez se vc postar o modelo das duas tabelas e as suas classes correspondentes a estas tabelas anime o pessoal a te ajudar.

[]'s
[/quote]

Então cara, no meu banco de dados está assim:

A minha classe Pessoa:

package pojo;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;

import annotations.Rotulo;

@Entity
@Rotulo(valor="Pessoa")
public class Pessoa implements Serializable{
	private static final long serialVersionUID = -5441754907875556638L;

	@Id
    @Column(name="PES_CODIGO")
    @SequenceGenerator(allocationSize=1, name = "SEQ", sequenceName="SEQ_PESSOA")
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ")
    @Rotulo(valor="Código")
    private int id;

    @Column(name="PES_RAZAOSOCIAL")
    @Rotulo(valor="Nome/Razão Social")
    private String razaoSocial;
    
    @Rotulo(valor="Tipo de Pessoa")
    @JoinColumn(name="TIPPES_CODIGO")
    @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    private TipoPessoa tipoPessoa;
    
    @Column(name="PES_EMAIL")
    @Rotulo(valor="Email")
    private String email;
    
    @Column(name="PES_NOMEFANTASIA", nullable=true)
    @Rotulo(valor="Nome Fantasia")
    private String nomeFantasia;
    
    @Column(name="PES_CNPJ", length=14, nullable=true)
    @Rotulo(valor="CPF/CNPJ")
    private String CNPJ;
    
    public Pessoa() {
    	tipoPessoa = new TipoPessoa();
	}
    
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Pessoa other = (Pessoa) obj;
        if (this.id != other.id) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        return getId();
    }
    
    @Override
    public String toString() {
    	return this.getRazaoSocial();
    }

    public int getId() {
        return id;
    }

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

    public TipoPessoa getTipoPessoa() {
        return tipoPessoa;
    }

    public void setTipoPessoa(TipoPessoa tipoPessoa) {
        this.tipoPessoa = tipoPessoa;
    }

    public String getCNPJ() {
        return CNPJ;
    }

    public void setCNPJ(String cnpj) {
        CNPJ = cnpj;
    }

    public String getRazaoSocial() {
        return razaoSocial;
    }

    public void setRazaoSocial(String razaoSocial) {
        this.razaoSocial = razaoSocial;
    }

    public String getNomeFantasia() {
        return nomeFantasia;
    }

    public void setNomeFantasia(String nomeFantasia) {
        this.nomeFantasia = nomeFantasia;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Minha classe PessoaUsuario:

package pojo;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;

import annotations.Rotulo;

@Entity
@Rotulo(valor="Usuário")
public class PessoaUsuario implements Serializable {
	private static final long serialVersionUID = 1060983168252431713L;
	
	@Id
	@JoinColumn(name="PES_CODIGO")
	@OneToOne(cascade=CascadeType.ALL)
	private Pessoa pessoa;
	

	@ManyToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="GRUUSO_CODIGO")
	@Rotulo(valor="Grupo de Usuário")
	private GrupoUsuario grupoUsuario;
	
	@Column(name="PESUSU_LOGIN", length=10, nullable=false)
	@Rotulo(valor="Login")
	private String login;
	
	@Column(name="PESUSU_SENHA", length=8, nullable=false)
	@Rotulo(valor="Senha")
	private String senha;
	
	public PessoaUsuario() {
		super();
	}
	
	public Pessoa getPessoa() {
		return pessoa;
	}

	public void setPessoa(Pessoa pessoa) {
		this.pessoa = pessoa;
	}
	
	
	public String getLogin() {
		return login;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public String getSenha() {
		return senha;
	}
	public void setSenha(String senha) {
		this.senha = senha;
	}

	public GrupoUsuario getGrupoUsuario() {
		return grupoUsuario;
	}
	public void setGrupoUsuario(GrupoUsuario grupoUsuario) {
		this.grupoUsuario = grupoUsuario;
	}

	@Override
	public String toString() {
		return this.login;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((pessoa == null) ? 0 : pessoa.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PessoaUsuario other = (PessoaUsuario) obj;
		if (pessoa == null) {
			if (other.pessoa != null)
				return false;
		} else if (!pessoa.equals(other.pessoa))
			return false;
		return true;
	}
	
	
}

Quando rodo a aplicação o JPA não gera a tabela pra mim e não um erro de jdbc de tabela não encontrada. Se eu crio ela na mão, ele cria os outros campo na tabela para mim, mas na Pessoa ele cria um campo BLOB que nem é chave primária.

Fazendo da forma que o felipeguerra sugeriu consegui resolver parcialmente o problema. Mas ta acontecendo dele executar dois inserts quando mando salvar uma PessoaUsuario, um para a classe PessoaUsuari e outro para a classe Pessoa, ambos com os mesmos dados. Nesse caso a Pessoa já estava previamente cadastrada e os dados são duplicados em um novo registro. Por isso eu gostaria de usar composição e não herança nesse caso específico.

Alguém sabe se existe uma forma de realizar isso por composição, sem a necessidade de realizar a herança.

Vamos fazer o seguinte. Vou postar aqui os meus mapeamentos. Veja se te ajuda:

SISTEMA

@Entity
@Table(name = "assistema")
  @NamedQueries({
  @NamedQuery(name="asSistema.searchAll",     query="select e from asSistema e "),
  @NamedQuery(name="asSistema.searchFgAtivo", query="select e from asSistema e where e.fgAtivo = :fgAtivo"),
  @NamedQuery(name="asSistema.searchById",    query="select e from asSistema e where e.idSistema = :idSistema")
})

public class asSistema implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)

    @Column(name = "id_sistema", nullable = false)
    private String idSistema;

    @Column(name = "nome", nullable = false)
    private String nome;

    @Column(name = "fg_ativo", nullable = false)
    private String fgAtivo;

    @OneToMany(mappedBy = "sistema")
    private List<asOperacao> operacaoList;

    @OneToMany( mappedBy="sistema" )
    private List<asSistemaModulo> sistemaModuloList;

   // gets e sets

MODULO

@Entity
@Table(name = "asmodulo")

  @NamedQueries({
  @NamedQuery(name="asModulo.searchAll",     query="select e from asModulo e "),
  @NamedQuery(name="asModulo.searchFgAtivo", query="select e from asModulo e where e.fgAtivo = :fgAtivo"),
  @NamedQuery(name="asModulo.searchById",    query="select e from asModulo e where e.idModulo = :idModulo")
})

public class asModulo implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)

    @Column(name = "id_modulo", nullable = false)
    private int idModulo;

    @Column(name = "descricao", nullable = false)
    private String descricao;

    @Column(name = "fg_ativo", nullable = false)
    private String fgAtivo;
    
    @OneToMany( mappedBy="modulo" )   
    private List<asSistemaModulo> sistemaModuloList;
    
    // GET e SET

E AGORA ( SISTEMA_MODULO ) SÓ PARA A CHAVE PRIMARIA

@Embeddable
public class asSistemaModuloPK implements Serializable {

    @Column(name = "sistema", nullable = false)
    private String sistema;
    
    @Column(name = "modulo", nullable = false)
    private int modulo;

    public asSistemaModuloPK() {
    }

ASSISTEMA_MODULO entity

@Entity
@Table(name = "assistema_modulo")
@NamedQueries
  ({
    @NamedQuery(name = "asSistemaModulo.searchAll",     query = "select e from asSistemaModulo e"),
    @NamedQuery(name = "asSistemaModulo.searchFgAtivo", query = "select e from asSistemaModulo e where e.fgAtivo = :fgAtivo"),
    @NamedQuery(name = "asSistemaModulo.searchById",    query = "select e from asSistemaModulo e where e.sistema = :sistema and e.modulo = :modulo")
  })

public class asSistemaModulo implements Serializable{
    
    @EmbeddedId
    private asSistemaModuloPK asSistemaModuloPK;
    
    @ManyToOne
    @JoinColumn(name="sistema", referencedColumnName="id_sistema",insertable=false,updatable=false) 
    private asSistema sistema;
    
    @ManyToOne
    @JoinColumn(name="modulo" , referencedColumnName="id_modulo",insertable=false,updatable=false)    
    private asModulo modulo;

Cara, me passa o teu email… eu te mando umas apostilas com vários exemplos de mapeamento.
Quem sabe nao resolver o teu problema…

Valeu cara! Vou te mandar uma mensagem pessoal com meu email. Se puder me enviar esse material ficarei grato!

Muito obrigado a todos. Consegui resolver o problema com o uso da anotação @PrimaryKeyJoinColumn, como explicado na documentação do hibernate:
http://www.hibernate.org/hib_docs/annotations/reference/en/html_single/#d0e1001

Minha classe PessoaUsuario ficou assim:

package pojo;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;

import annotations.Rotulo;

@Entity
@Rotulo(valor="Usuário")
public class PessoaUsuario implements Serializable {
	private static final long serialVersionUID = 1060983168252431713L;
	
	@Id
	@Column(name="PES_CODIGO")
	@Rotulo(valor="Código")
	private int id;
	
	@PrimaryKeyJoinColumn(name="PES_CODIGO")
	@OneToOne
	@Rotulo(valor="Pessoa")
	private Pessoa pessoa;
	
	@ManyToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="GRUUSO_CODIGO")
	@Rotulo(valor="Grupo de Usuário")
	private GrupoUsuario grupoUsuario;
	
	@Column(name="PESUSU_LOGIN", length=10, nullable=false)
	@Rotulo(valor="Login")
	private String login;
	
	@Column(name="PESUSU_SENHA", length=8, nullable=false)
	@Rotulo(valor="Senha")
	private String senha;
	
	public PessoaUsuario() {
		pessoa = new Pessoa();
	}
	

	public int getId() {
		return id;
	}


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


	public Pessoa getPessoa() {
		return pessoa;
	}


	public void setPessoa(Pessoa pessoa) {
		this.pessoa = pessoa;
	}


	public String getLogin() {
		return login;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public String getSenha() {
		return senha;
	}
	public void setSenha(String senha) {
		this.senha = senha;
	}

	public GrupoUsuario getGrupoUsuario() {
		return grupoUsuario;
	}
	public void setGrupoUsuario(GrupoUsuario grupoUsuario) {
		this.grupoUsuario = grupoUsuario;
	}

	@Override
	public String toString() {
		return this.login;
	}


	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((pessoa == null) ? 0 : pessoa.hashCode());
		return result;
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PessoaUsuario other = (PessoaUsuario) obj;
		if (pessoa == null) {
			if (other.pessoa != null)
				return false;
		} else if (! (pessoa.getId() == other.pessoa.getId()) )
			return false;
		return true;
	}
	
	
}

Dessa forma, ele já faz tudo pra mim, alimentando meu objeto Pessoa certinho e criando no banco um campo PES_CODIGO que é pk e fk ao mesmo tempo.

Boa noite Renato.
Desculpe reativar este tópico mas estou com o mesmo problema que você passou e não estou conseguindo resolver. Você teria algum exemplo para me ajudar?
As minhas tabelas são cliente e endereço onde um cliente tem um endereço. A chave de cliente é a chave do endereço. é um relacionamento um para um.
Por favor, preciso de ajuda
Obrigado