Relacionamento com uma tabela de chave composta. Insert não funciona (JPA)

Fala ai galera, tudo certinho??

Bom, ja fazem 3 noites que eu estou passando em claro pra tentar resolver um problema e não obtive êxito, então resolvi vir aqui pedir ajuda de vcs.
Meu problema é bem estranho, vou tentar colocar bem detalhado o que ta passando pra que vcs possam me ajudar.

Vamos la… tenho os seguintes relacionamentos. Tenho uma tabela TB_MODULO com campos (ID_MODULO, DESCRICAO, …), e tenho outras duas tabelas

TB_USUARIO (ID_USUARIO, ID_PERFIL, ID_MODULO, …),
TB_PERFIL (ID_PERFIL, ID_MODULO)

Ou seja, as duas tabelas são relacionadas a um determinado módulo, e a tabela TB_USUARIO esta relacionada a um perfil.

Usando o gerador de entidades do NetBeans e do Eclispe, ambos me geram o seguinte mapeamento. Lembrando que estou usando mapeamentos Bidirecionais

TB_MODULO:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "modulo", fetch = FetchType.LAZY)
private Collection<Usuario> usuarioCollection;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "modulo", fetch = FetchType.LAZY)
private Collection<Perfil> perfilCollection;
//Demais mapeamentos

TB_PERFIL:

@Id
@Basic(optional = false)
@Column(name = "ID_PERFIL")
private BigDecimal idPerfil;
@Id
@Basic(optional = false)
@Column(name = "ID_MODULO")
private BigDecimal idModulo;

@JoinColumn(name = "ID_MODULO", referencedColumnName = "ID_MODULO", insertable = false, updatable = false)
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Modulo modulo;
//Demais mapeamentos

TB_USUARIO:

@Id
@Basic(optional = false)
@Column(name = "ID_USUARIO")
private BigDecimal idUsuario;
@Id
@Basic(optional = false)
@Column(name = "ID_MODULO")
private BigDecimal idModulo;

@JoinColumns({
    @JoinColumn(name = "ID_PERFIL", referencedColumnName = "ID_PERFIL"),
    @JoinColumn(name = "ID_MODULO", referencedColumnName = "ID_MODULO", insertable = false, updatable = false)})
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Perfil perfil;

@JoinColumn(name = "ID_MODULO", referencedColumnName = "ID_MODULO", insertable = false, updatable = false)
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Modulo modulo;
//Demais mapeamentos

Com isso, minha aplicação funciona perfeitamente, quando faço buscas em usuário, ele traz o perfil associado, se busco por módulo ele tambem traz os seus respectivos usuários e perfis, se
busco por perfil ele traz todos os usuários associados aquele perfil ta funcionando certinho, o problema é: Não estou conseguindo persistir ou atualizar um novo usuário, acusa um erro

Olhando no sql de insert que é gerado, (lembrando que fiz os testes tanto com Hibernate quanto com TopLink) ele não seta na query o campo ID_PERFIL para ser persistido, vejam:

para não deixar dúvida, vou postar tambem o trecho do código onde carrego os dados do usuário pra ser persistido.

Perfil perfil = (Perfil) meuPerfilSelecionado //Antes que perguntem, sim.. os dados do perfil estão sendo carregados
Usuario usuario = new Usuario ();
usuario.setIdUsuario(1);
usuario.setIdModulo(1); //Tambem existe o múdulo 1
usuario.setPerfil(perfil);
usuario.setDsUsuario("usuario");
usuario.setDsPassword("****");
usuario.setDsLogin("login");

Bom pessoal, postei esse trecho de código só pra terem noção de como estou fazendo, mais o problema não esta no modo de carregar os dados e nem de persistir, acredito que o problema
esta em algum mapeamento nos relacionamentos das tabelas, penso eu que o “@JoinColumns” na tabela de usuário que faz relação a de perfil, não esta agindo como deveria agir. Como falei,
pra consultas, esta perfeito, funciona normal, porém não consigo persistir ou atualizar nenhum usuário por ele não associar o ID_PERFIL da tabela de usuário com a de perfil, e como ele faz parte da chave composta de usuário, claro que vai dar erro. Agora a questão é: porque essa associação não esta sendo feita, alguém pelo amor de Deus poderia me ajudar?

Desde já agradeço a todos.

Abraços

Amigo, acredito que se você já tem o objeto “MODULO” nos objetos da tabela “PERFIL” e “USUARIO”, você não precisa ter o “BigDecimal idModulo” tambem.

Tente retirar o idModulo desses objetos e efetuar o relacionamento apenas no objeto “MODULO”.

Ae quando você for criar o usuário, pesquise o modulo antes de setar no perfil do usuario, assim já será feito o relacionamente corretamente.

Espero que funcione, senao, qualquer coisa fala ae

Kra o problema é q vc está mapeando a chave composta do modo errado.

JPA só suporta entidades com uma chave única. Quando vc precisar mapear uma relação q possui uma
chave composta vc deve usar uma outra classe p/ representar essa chave.

@Embeddable
public class MinhaPK implements Serializable {
  private String nome;
  private String cpf;

  public MinhaPK() {}

  // Getters e Setters

  public boolean equals(Object o) {
    // Até onde me lembro esse método é obrigatório nesse tipo de classe.
  }

  public int hashCode() {
    // Até onde me lembro esse método é obrigatório nesse tipo de classe.
  }
}

E na sua entidade:

@Entity
public class MinhaEntidade implements Serializable {
  @EmbeddedId
  public MinhaPK id;

  // O resto do seu código...
}

Tenta isso ai!!

dev.rafael valew pela ajuda.

Mais bem… vamos lá.

Bom, antes de começar, eu resolvi dar uma mudada no meu relacionamento, minha TB_USUARIO não se relaciona mais com TB_MODULO, até pq eu ja tenho
uma relação de TB_MODULO na minha tabela TB_PERFIL, e como minha TB_USUARIO se relaciona a TB_PERFIL eu matei esse relacionamento, ficou assim

TB_USUARIO (ID_USUARIO, ID_PERFIL, …),
TB_PERFIL (ID_PERFIL, ID_MODULO) onde (ID_PERFIL, ID_MODULO) são chaves primárias compostas sendo que meu ID_MODULO é uma FK para TB_MODULO…

Quanto a retirar o idModulo deu certinho, realmente eu não precisava dele la, ja que eu tinha um relacionamento com a tabela TB_MODULO.

Agora quanto a usar o @Embeddable, em partes esta dando certo, na verdade funciona a questão é que: Eu, pra poder testar se realmente estava certo o mapeamento,
habilitei para auto-gerar as minhas tabelas no banco de dados e seus relacionamentos. Maravilha, ele gera as tabelas normais mais tem um porém. Na minha tabela
TB_PERFIL , ele configura somente o ID_PERFIL como PK, e o ID_MODULO ele coloca somente como FK, erradooooo os dois IDs formam a chave composta da minha
tabela, então o ID_MODULO tambem deviria fazer parte da minha PK. Abaixo segue o código de como estou fazendo o relacionamento.

@Embeddable
public class TbPerfilPK implements Serializable {

    private BigInteger idPerfil;

    @ManyToOne
    @JoinColumn(name = "ID_MODULO", insertable = false, updatable = false)
    //@PrimaryKeyJoinColumn(name = "ID_MODULO", referencedColumnName = "ID_MODULO")
    // Ja tentei com esse PrimaryKeyJoin mais tambem não resolveu nada
    private TbModulo tbModulo;

   //gets , sets, hascode, equals...........
}
@Table(name = "tb_perfil")
public class TbPerfil implements Serializable {

    @EmbeddedId
    private TbPerfilPK tbPerfilPK;

    //gets , sets, hascode, equals...........
}

Será que to fazendo algo de errado?

Outra coisa, sabe como eu faria se fosse usar o @IdClass ao invés do @EmbeddedId, acho que o código fica mais coerente. Tentei aqui mais não deu certo, sai um erro

Fiz tudo certinho como manda a documentação mais não foi não…

Bom, é isso… se puder me ajudar ficarei muito grato, valew

Ok, minha opinião, o q vc está fazendo de errado é o uso de uma chave composta e um erro ainda maior
é adicionar um chave estrangeira como parte da sua chave primária.

Na boa, chaves compostas ocupam mais espaço no banco, são mais lentas p/ indexar, mais complexas de
se manipular e, geralmente, muito atada a sua lógica. Olha vc ai já tento problemas!!

Se algum professor de DB cabeça dura ler isso, por favor me perdoe! Mas a verdade é que chaves
compostas só ñ são um erro quando o banco já existe e altera-lo ñ é uma opção.

Até onde eu pesquisei na internet nenhum provedor JPA supporta esse tipo de mapeamento.

Cara… vou te falar a verdade… rodei o mundo web inteiro essas duas semanas passadas, e não encontrei nenhuma solução para o meu problema,
encontrei inclusive uma galera que esta passando por algo parecido, e que não acham solução, tópicos desse tipo que foram inicializados e nunca finalizados
são o que mais se encontram…

Eu particularmente não acho que chaves compostas são um ERRO, elas existem para ser usadas, caras grandes como Oracle não implementaria ERROs
desse tipo em seu banco de dados… Porém… cheguei a uma conclusão… inclusive é a conclusão que quem procura por esse tipo de solução acaba
tendo… Mew… não vale apena usar chave composta principalmente quando ela tem uma FK, se está usando alguma implementação JPA. Se quer uma coisa funcional e transparente
use chaves simples ou até mesmo composta, desde que não tenha FK na composição da chave. E digo mais…, se acham que eu estou tomando conclusão precipitada faça um teste,
aqueles que tem mapeamento de relacionamento com chaves primárias compostas por FK… ativem o gerador de tabelas automático no persistence.xml, crie uma base
de teste, mandem executar a aplicação, e depois vá checar o banco de dados pra ver como ficou os relacionamentos… garanto que não vão achar chave composta
nenhuma rs…

Depois dessa conclusão, eu excluí todas as minhas chaves compostas, até pq estou criando a base de dados agora, e não é algo que ja tinha pronto, muito mais fácil de
mexer agora do que depois rs… Apesar de eu não ter tantas chaves compostas, as que eu tinha foram eliminadas, meu sistema está funcionando do mesmo jeito, ta muito mais
transparente, e as chaves não estão me fazendo falta nenhuma, e nem causando algum tipo de problemas.

Enfim… como vc argumentou dev.rafael, se esta criando um banco de dados desde o início, nem pense em usar chaves desse tipo, pois terão grande problema no decorrer
do projeto… não vale a pena… Porém… se estão usando uma base ja existente que não pode ser alterada… Boa sorte amigo aiuaauhuahhau…

É isso ai galera… valew pela ajuda

Outro post sobre chaves compostas? Quero opinar também! :smiley:

Bem, por experiência, eu falo: não use chaves compostas, se puder. Evite-as ao máximo. Só trazem problemas.
Na teoria é muito bonito, mas na prática as chaves compostas são uma porcaria. Eu já mapeei várias classes que necessitavam de chave composta, foi sofrido mas funcionou. Mas eu digo, é um lixo, é bem mais fácil e prático (além de transparente) criar um campo pra representar o ID e fazer um “unique constraint” nos campos que seriam a sua chave composta.

O problema, eu sei, são os chefes cabeça-dura que querem seguir a teoria passo à passo, então o seu chefinho pensador vai achar ruim quando você falar isso… Ignore, e mostre pra ele quão melhor é fazer tudo sem as malditas chaves compostas!