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

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?

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;
}

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

vtr002 Posta como ficou seu resultado.

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

[code]
@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

}[/code]

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;

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

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;

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

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

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;

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

“EntityNotFoundException”

Foi resolvido seu problema ?

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

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?

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

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)

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

[code]@Entity
public class Operacoes {
@EmbeddedId
private OperacoesPK pk;

private String operacao;[/code]

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();

ErroException 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.

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();

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?

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.