[Resolvido] Hibernate PK/FK composta que aponta para outra PK composta

20 respostas
vtr002

Bem vamos a explicação
Tenho uma tabela A com uma chave composta
tenho uma Tabela B que a chave primaria aponta para tabela A e tem mais um item na chave

Por exemplo
Table Nota Fiscal
-PK/FK EMPRESA
-PK/FK NUMERO NOTA

Table Nota Fiscal Item
-PK/FK EMPRESA
-PK/FK NUMERO NOTA
-PK Nr Item

Como faço isso por EmbedId no hibernate?

20 Respostas

wagnerfrancisco

Implemente a chave normalmente, sem considerar a relação. Depois, na entidade, mapeie a relação como updatable=false e insertable = false.

PK Nota Fiscal:

@Embeddable
public class NotaFiscalPK implements Serializable {
    @Column(name = "empresa")
    private Integer empresa;
    @Column(name = "numero_nota")
    private Integer numeroNota;

    //getters and setters

    //equals e hashcode - EH OBRIGATORIO NESTE CASO!
}

PK Nota Fiscal Item:

@Embeddable
public class NotaFiscalItemPK implements Serializable {
    @Column(name = "empresa")
    private Integer empresa;
    @Column(name = "numero_nota")
    private Integer numeroNota;
    @Column(name = "numero_item")
    private Integer item;

    //getters and setters

    //equals e hashcode - EH OBRIGATORIO NESTE CASO!
}

Classe do item da nota:

public class NotaFiscalItem {
    @EmbeddedId
    private NotaFiscalItemPK pk;
    @ManyToOne
    @JoinColumns({
       @JoinColumn(name = "empresa", referencedColumnName="empresa", insertable = false, updatable = false),
       @JoinColumn(name = "numero_nota", referencedColumnName="numero_nota", insertable = false, updatable = false)
    })
    private NotaFiscal notaFiscal;
}
vtr002

Eu consegui, mas fiz um pouco diferente.
coloquei as join columns dentro da PK.
Por hora vou dar como resolvido
Valeu

N

vtr002 Posta como ficou seu resultado.

vtr002
public class NotaFiscalItem {
    @EmbeddedId
    private NotaFiscalItemPK pk;
//Esta parte em vez de estar na classe NotaFiscalItem ficou na PK
    @ManyToOne
    @JoinColumns({
       @JoinColumn(name = "empresa", referencedColumnName="empresa", insertable = false, updatable = false),
       @JoinColumn(name = "numero_nota", referencedColumnName="numero_nota", insertable = false, updatable = false)
    })
    private NotaFiscal notaFiscal;
}
Ficando assim
@Embeddable
public class NotaFiscalPK implements Serializable {

 @ManyToOne
    @JoinColumns({
       @JoinColumn(name = "empresa", referencedColumnName="empresa", insertable = false, updatable = false),
       @JoinColumn(name = "numero_nota", referencedColumnName="numero_nota", insertable = false, updatable = false)
    })
    private NotaFiscal notaFiscal;
    @Column(name = "numero_nota")
    private Integer numeroNota;

    //getters and setters


}
N

Eu consegui fazer funcionar… mas tenho uma tabela que a PK/FK dela tem o mesmo nome que uma PK/FK da outra tabela que é FK da primeira rsrsr… vou exemplificar

Ex:
Table Empresa
PK cod_empresa

Table Operacoes
PK cod_operacao
PK/FK cod_empresa

Table Venda
PK controle
PK/FK cod_empresa
FK cod_operacao (minha duvida é aqui, acredito q ele tenta mapear um cod_empresa aqui tambem, ja que a tabela operaçoes tambem tem um cod_empresa)

Fiz assim:

Esse mapeamento Funciona!

@Embeddable public class OperacoesPK implements Serializable { @Column(name="cod_operacao") private int codOperacao; @Column (name="cod_empresa") private int codEmpresa;

@Entity public class Operacoes { @EmbeddedId private OperacoesPK pk;

Aqui por usar a mesma FK como PK ta dando pau

@Embeddable public class VendaPK implements Serializable{ private Long controle; private String serie; @Column(name="cod_empresa") private int codEmpresa;

@Entity @Table(name="vendas") public class Venda { @EmbeddedId private VendaPK pk; @OneToOne @JoinColumns({ @JoinColumn(name="cod_operacao", insertable=false, updatable=false), @JoinColumn(name="cod_empresa", insertable=false, updatable=false)//acho que teria que ser só o joincolumn de cima, mas se deixar só ele dá erro porque a PK de operaçoes sao duas colunas }) private Operacoes operacao;

N

Esqueci de falar do erro. Quando faço “select o from Operacoes” ele me retorna o resultado correto, com 30 registros. Porem quando eu faço “select v from Venda” ele faz duas selects, uma buscando as vendas e outra buscando as operaçoes.
nessa hora ele retorna o erro javax.persistence.EntityNotFoundException: Unable to find br.com.virage.mobile.modelo.Operacoes with id br.com.virage.mobile.modelo.OperacoesPK@cd8 o que é estranho, porque todos os cod_operacoes na tabela vendas estao cadastrados na tabela operacoes, assim como seu respectivo cod_empresa

vtr002

Você está certo, falta o Join Column e mais um detalhe

@Embeddable
public class VendaPK implements Serializable{
	private Long controle;
	private String serie;
//Precisa ser Join Column
	@Column(name="cod_empresa")
//não é um int é um objeto da Classe Entidade Empresa
	private int codEmpresa;
N

na verdade eu havia feito assim e o @joincolumn tanto na classe FK quanto na classe normal… funciona…

meu erro está na hora da relaçao de venda com operacoes… se eu tirar operacoes da classe Venda lista normalmente

vtr002

o Join column e a classe é la mesmo. e falta o join column com a entidade empresa também na OperaçõesPK eles tem que estar falando da mesma coisa

N

coloquei como falou… faço a busca em todas as empresas… funciona… faço a busca em todas as operacoes… funciona… quando faço a busca em todas as Vendas ainda da erro (o hibernate monta a select tambem nas tabelas que estao relacionadas)

como se nao tivesse achando uma empresa com um determinado id. Todos os cod_empresa que tenho na tabelas vendas estao presentes na tabela empresa

classe e PK Vendas agora.

public class Venda { @EmbeddedId private VendaPK pk; @OneToOne @JoinColumns({ @JoinColumn(name="cod_operacao", insertable=false, updatable=false), @JoinColumn(name="cod_empresa", insertable=false, updatable=false) }) private Operacoes operacao;
Nao preciso instanciar um objeto Empresa dentro de venda nao né… ja que instanciei em minha PK

@Embeddable public class VendaPK implements Serializable{ private Long controle; private String serie; @OneToOne @JoinColumn(name="cod_empresa") private Empresa codEmpresa;

R

Verifica se voce colocou o @Entity na classe venda, ou se voce mapeou certo nos seus arquivos xml.

“EntityNotFoundException”

R

Foi resolvido seu problema ?

Se foi resolvido, favor postar a solução para ajudar os demais da comunidade.

N

Está com a anotaçao sim… tanto é q se eu deixar a classe sem vinculos com empresas e operaçoes ela funciona… há algo errado no relacionamento… o hibernate monta as selects na from vendas, from operacoes e from empresas (pra fazer o select em vendas ele levanta tambem a select dos relacionamentos) só q ele fala q algum ID nao foi encontrado… eu ja conferi todos…!! mas acho que sei o q ocorre…
Na minha tabela Venda eu nao sou obrigado a ter registro de todas as empresas … posso ter empresa que ainda nao vendeu… nao seria isso?? ele procura uma venda de uma empresa e nao encontra?

N

Meu mapeamento está igual o postado acima… nao vou postar de novo para aproveitar espaço

fiz o debug pelo log4j e percebi o seguinte…
minha tabela operaçoes tem a PK composta por cod_empresa e cod_operacao
na tabela vendas eu tenho minha PK composta por controle e cod_empresa
eu percebi q na primeira linha da minha tabela vendas… o cod_operacao=75 aí ele pega esse id e faz uma busca em empresa com o mesmo id… acredito que porque cod_empresa tambem faz parte da minha PK de cod_operacao

vtr002

Acho que descobri o seu problema,
Voce mapeou errado a relação
Só existe uma venda por empresa?
se sim então OneToOne
se não é ManyToOne(varias vendas por empresa)

N

Realmente minha relaçao é manytoone.. porem o problema ainda nao é esse..

Na tabela Vendas tenho uma FK cod_operacao, cod_operacao é parte da PK da tabela operacoes.. a outra parte ta PK de operacoes é cod_empresa.. tenho uma operacao com cod_operacao=75
quando faço From Operacoes me retorna tudo certinho... mas quando faço From Vendas ... ele pega o cod_operaçao = 75 e faz uma busca na tabela empresas com esse codigo..

isso que nao entendo.. vou postar como está agora.

OperacoesPK
@Embeddable
public class OperacoesPK implements Serializable {
	@Column(name="cod_operacao")
	private int codOperacao;
	@ManyToOne
	@JoinColumn (name="cod_empresa")
	private Empresa codEmpesa;
Operacoes
@Entity
public class Operacoes {
	@EmbeddedId
	private OperacoesPK pk;

	private String operacao;
Venda
@Entity
@Table(name="vendas")
public class Venda {
	@EmbeddedId
	private VendaPK pk;
	@Column(name="total_servicos")
	private BigDecimal totalServicos;
	@Column(name="total_produtos")
	private BigDecimal totalProdutos;
	@ManyToOne
	@JoinColumns({
	@JoinColumn(name="cod_operacao", insertable=false, updatable=false),
	@JoinColumn(name="cod_empresa", insertable=false, updatable=false)
	})
	private Operacoes operacao; // Comentando Operacoes assim como suas anotaçoes.. funciona normalmente
	@Temporal(TemporalType.DATE)
	private Calendar emissao = Calendar.getInstance();
Erro
Exception in thread "main" javax.persistence.EntityNotFoundException: Unable to find br.com.virage.mobile.modelo.Empresa with id 75
Ele pega o codigo da operacao que está na tabela vendas (75 no caso) e faz uma busca na tabela empresa com esse valor... nao entendo o porque.
vtr002

Nesse caso de varios joincolumn voce tem que por o referencedcolumn

@Entity
@Table(name="vendas")
public class Venda {
	@EmbeddedId
	private VendaPK pk;
	@Column(name="total_servicos")
	private BigDecimal totalServicos;
	@Column(name="total_produtos")
	private BigDecimal totalProdutos;
	@ManyToOne
	@JoinColumns({
	@JoinColumn(name="cod_operacao", referencedColumnName="cd_operacao",insertable=false, updatable=false),
	@JoinColumn(name="cod_empresa", referencedColumnName="cd_empresa", insertable=false, updatable=false)
	})
	private Operacoes operacao; // Comentando Operacoes assim como suas anotaçoes.. funciona normalmente
	@Temporal(TemporalType.DATE)
	private Calendar emissao = Calendar.getInstance();
N

Isso mesmo!!! agora sim funcionou…Mas qual a necessidade do referencedcolumn se agente ja itentifica ela com o name?
Pra ficar 100% agora… o hibernate faz uma select pra tabela vendas… OK… mas está fazendo uma select pra cada cod_empresa que tenho… e um select pra cada cod_operacao (mais de 80)… dá pra mudar isso?

vtr002

O referencedColumnName serve para o hibernate saber qual coluna exatamente da tabela de vendas esta ligada na sua relação, não basta ter o mesmo nome.

Essa parte dos selects eu não entendi sua duvida. Eu aqui uso o Oracle e ele faz apenas um select com Inner joins ligando as tabelas. e por causa do inner join ele faz um select em cada tabela para criar o objeto da outra tabela. é Meio estranho mas é assim mesmo.

N

assim… coloquei pro hibernate mostrar a SQL no console…executo o comando pra me retornar uma lista de vendas…

Passo 1: ele faz um select para a tabela vendas - OK

Passo 2: ele faz 5 selects na tabela empresas, porque está relacionada a tabela vendas e possuo 5 empresas;

Passo 3 : ele faz mais de 80 selects na tabela Operacoes, ja que tenho mais de 80 no banco… acredito que ele deveria fazer só uma vez a select nessa tabela…

Criado 4 de dezembro de 2012
Ultima resposta 17 de dez. de 2012
Respostas 20
Participantes 4