[RESOLVIDO]JPA: Chave primária composta, formada por chaves estrangeiras "repetidas"

Olá pessoal!
Estou com um problema no uso da JPA para a representação de um relacionamento maluco (Vide anexo):

CONNECTION representa a existência de uma conexão entre duas formas geométricas (não necessáriamente distintas).
LINK representa a ligação entre dois pontos de duas formas geométricas.
Uma conexão pode ser estabelecida por um ou mais links.

Meu problema está na hora de representar no JPA a classe para a entidade LINK. Abaixo segue o que já tentei fazer:

Tenho criada uma classe LinkPK anotada com @Embedded.

1)Uma primeira tentativa, foi utilizar o @EmbeddedId. Nesta abordagem, as classes ficaram assim:

@Embeddable
public class LinkPK implements java.io.Serializable {
	private static final long serialVersionUID = 5114770736427051887L;
	
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="connectionId")
	private Connection connection;
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="dotIdTo")
	private Dot dotTo;
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="dotIdFrom")
	private Dot dotIdFrom;

	...
}

@Entity
@Table(name="LINK")
public class Link implements java.io.Serializable {
	private static final long serialVersionUID = -2439429883405985924L;
	
	@EmbeddedId
	private LinkPK id;
	...
}

@Entity
@Table(name="CONNECTION")
public class Connection implements java.io.Serializable {
	private static final long serialVersionUID = 6486629810580504521L;
	
	@Id
	@GeneratedValue
	private long id;
	
	@OneToMany(mappedBy="connection")
	private List<Link> links;

	...
}

@javax.persistence.Entity
@Table(name="DOT")
public class Dot implements java.io.Serializable {
	private static final long serialVersionUID = 9067572139934777767L;

	@Id
	@GeneratedValue
	private long id;

	@OneToMany(mappedBy="dotTo")
	private List<Link> toRole;
	@OneToMany(mappedBy="dotFrom")
	private List<Link> fromRole;
	
	...
}

O problema com essa abordagem foi que o Hibernate não consegue criar as tabelas, pois ele diz não encontrar os “mappedBy” na classe Link. Assumi que isso se deu por conta dos “mappedBy” estarem declarados na classe LinkPK, e abortei esta estratégia.

2)A segunda idéia foi utilizar o @IdClass. Aí só a classe Link e LinkPK foram alteradas:

@Embeddable
public class LinkPK implements java.io.Serializable {
	private static final long serialVersionUID = 5114770736427051887L;
	
	private Connection connection;
	private Dot dotTo;
	private Dot dotIdFrom;

	...
}

@Entity
@Table(name="LINK")
@IdClass(LinkPK.class)
public class Link implements java.io.Serializable {
	private static final long serialVersionUID = -2439429883405985924L;
	
	@Id
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="connectionId")
	private Connection connection;
	@Id
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="dotIdTo")
	private Dot dotTo;
	@Id
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="dotIdFrom")
	private Dot dotIdFrom;
	
	...
}

Neste caso, o Hibernate até cria as tabelas, porém ele não consegue definir as foreign keys da tabela LINK. Segue o log na hora da geração das FK:

ERROR: [] Unsuccessful: alter table LINK add constraint FKC58BCE062FC9C056 foreign key (connection) references CONNECTION 2010-08-06 09:50:43,713 [main] org.hibernate.tool.hbm2ddl.SchemaExport ERROR: [] Column 'CONNECTION.id' is not the same data type as referencing column 'LINK.connection' in foreign key 'FKC58BCE062FC9C056'. 2010-08-06 09:50:43,713 [main] org.hibernate.tool.hbm2ddl.SchemaExport ERROR: [] Unsuccessful: alter table LINK add constraint FKC58BCE0656EB087F foreign key (dotTo) references DOT 2010-08-06 09:50:43,713 [main] org.hibernate.tool.hbm2ddl.SchemaExport ERROR: [] Column 'DOT.id' is not the same data type as referencing column 'LINK.dotTo' in foreign key 'FKC58BCE0656EB087F'. 2010-08-06 09:50:43,713 [main] org.hibernate.tool.hbm2ddl.SchemaExport ERROR: [] Unsuccessful: alter table LINK add constraint FKC58BCE0650B2A88E foreign key (dotTo) references DOT 2010-08-06 09:50:43,713 [main] org.hibernate.tool.hbm2ddl.SchemaExport ERROR: [] Column 'DOT.id' is not the same data type as referencing column 'LINK.dotTo' in foreign key 'FKC58BCE0650B2A88E'. 2010-08-06 09:50:43,729 [main] org.hibernate.tool.hbm2ddl.SchemaExport

Vou replicar o erro aqui…

Oi Mantu,

E se você considerasse LinkPK como uma outra entidade?
Esse ManyToOne dentro da PK fica muito esquisito…

[]´s

O problema é que vc está usando compositeID erroneamente, no composite id vc só terá os IDs…
A referência para as classes deve ficar na classe que possui o id (link) e não na classe id (linkPk), a classe id só tem ids (linkId, dotFrom e dotTo)

Seu relacionamento deve ficar assim:

MAIN

package br.com.livro.javapersistence.others.model.guj.ajuda.mantu;

import org.hibernate.Session;

import br.com.livro.javapersistence.commons.Execucao;
import br.com.livro.javapersistence.commons.util.HibernateUtil;

public class CriacaoChavesCompostas implements Execucao {

	public static void main(String[] args) {
		CriacaoChavesCompostas criacaoChavesCompostas = new CriacaoChavesCompostas();
		criacaoChavesCompostas.execute(HibernateUtil.getSessionFactoryAnnotation(true, criacaoChavesCompostas.getAnnotatedClasses()).openSession());
	}

	public void execute(Session session) {
		System.out.println("Sessao aberta");
	}

	@Override
	public Object[] getAnnotatedClasses() {
		return new Object[] { LinkPK.class, Link.class, Connection.class, Dot.class };
	}


}

DOT

package br.com.livro.javapersistence.others.model.guj.ajuda.mantu;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@javax.persistence.Entity
@Table(name = "DOT")
public class Dot implements java.io.Serializable {

	private static final long serialVersionUID = 9067572139934777767L;

	@Id
	@GeneratedValue
	@Column(name = "dotId")
	private long id;

	@OneToMany(mappedBy = "dotTo")
	private List<Link> toRole;

	@OneToMany(mappedBy = "dotIdFrom")
	private List<Link> fromRole;

	// get and set
}

LINKPK

package br.com.livro.javapersistence.others.model.guj.ajuda.mantu;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class LinkPK implements java.io.Serializable {

	private static final long serialVersionUID = 5114770736427051887L;

	@Column
	private long linkId;
	@Column
	private long dotFrom;
	@Column
	private long dotTo;

	/**
	 * @param linkId
	 * @param dotFrom
	 * @param dotTo
	 */
	public LinkPK(long linkId, long dotFrom, long dotTo) {
		this.linkId = linkId;
		this.dotFrom = dotFrom;
		this.dotTo = dotTo;
	}
	//get and set

}

LINK

package br.com.livro.javapersistence.others.model.guj.ajuda.mantu;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.ForeignKey;

@Entity
@Table(name = "LINK")
public class Link implements java.io.Serializable {

	private static final long serialVersionUID = -2439429883405985924L;

	@EmbeddedId
	private LinkPK id;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "connectionId")
	@ForeignKey(name = "FK_CONNECTION_ID")
	private Connection connection;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "dotId", insertable = false, updatable = false)
	@ForeignKey(name = "FK_DOTTO_ID")
	private Dot dotTo;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "dotId", insertable = false, updatable = false)
	@ForeignKey(name = "FK_DOTIDFROM_ID")
	private Dot dotIdFrom;

	
}

Connection

package br.com.livro.javapersistence.others.model.guj.ajuda.mantu;

import java.util.List;

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

@Entity
@Table(name="CONNECTION")
public class Connection implements java.io.Serializable {

	private static final long serialVersionUID = 6486629810580504521L;
	
	@Id
	@GeneratedValue
	@Column(name ="connectionId")
	private long id;
	
	@OneToMany(mappedBy="connection")
	private List<Link> links;

	//get and set
}

Ps. Uma boa prática é vc dar nome nas ForeignKey

	@ForeignKey(name = "FK_CONNECTION_ID")

Aqui funcionou ehehhe


INFO: schema export complete
Sessao aberta

Abraços

[quote=davidbuzatto]Oi Mantu,

E se você considerasse LinkPK como uma outra entidade?
Esse ManyToOne dentro da PK fica muito esquisito…

[]´s[/quote]
Eu também achei, mas não posso mudar muito a modelagem porque é legada…

lelodois, na sua versão da classe LinkPK, vc pos o campo “linkId” mas você, na verdade, quis dizer “connectionId”? Caso contrário, não bate com a modelagem (Esse id vem é fk da entidade CONNECTION).

Vou testar aqui a sua proposta assumindo que o linkId é connectionId. Depois aviso se funcionou.

tks

[quote=Mantu]lelodois, na sua versão da classe LinkPK, vc pos o campo “linkId” mas você, na verdade, quis dizer “connectionId”? Caso contrário, não bate com a modelagem (Esse id vem é fk da entidade CONNECTION).

Vou testar aqui a sua proposta assumindo que o linkId é connectionId. Depois aviso se funcionou.

tks[/quote]

Verdade… confundi.
O que importa é ter a referência na classe que possui o id e não na classe id, a classe id deve ter somente os ids :wink:

abrs

Thanks lelodois, funcionou!
Só tive que colocar insertable/updatable false pro connectionId (Estava criando um campo a mais na tabela)

[quote=Mantu]Thanks lelodois, funcionou!
Só tive que colocar insertable/updatable false pro connectionId (Estava criando um campo a mais na tabela)[/quote]

:wink:

1 curtida