Problema TopLink, chave estrangeira NULL!

Fala Pessoal, blz ?

Estou com um problema com Toplink que não consigo resolver. Gostaria de um help de vcs.

Por exemplo: Criei uma classe Pedido e uma PedidoItem. Pedido tem vários PedidoItem, e PedidoItem tem apenas um Pedido. Este relacionamento esta Bidirecional, sendo assim, meu relacionamento esta conforme abaixo:

Classe Pedido:

[code]
private Integer id;
private String descricao;

@OneToMany(mappedBy="pedido", cascade=CascadeTypo.ALL) 
private List<PedidoItem> pedidosItens;

//Getters e Setters[/code]

Classe PedidoItem (aqui apenas adicionou o atributo pedido):

[code]
private Integer id;
private String item;
private PedidoItem pedido;

//Getters e Setters[/code]

Quando executo minha aplicação teste, no banco de dados (estou usando o bd MySQL) esta criando na tabela de PedidoItem com os seguintes campos:

Ao incluir registros de PedidoItem o campo Pedido_Id esta ficando NULO.

Verificando os comandos SQL no output vejo que o TopLink busca o id do pedido para poder gravar os pedidosItens, porém isso não esta ocorrendo. Já efetuei vários testes com relação as anotações (JoinColumn no pedidoItem, etc…), de várias formas e nada.
Já troquei as versões do TopLink, mas continuou o problema.

Por acaso vc tem alguma ideia do que pode estar ocorrendo ?

Obrigado por enquanto !

Tenta usar isto:

[quote]
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name=“ID_PEDIDO”,nullable=false)
private Pedido pedido;[/quote]

[quote=danieldestro]Tenta usar isto:

[quote]
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="ID_PEDIDO",nullable=false)
private Pedido pedido;[/quote][/quote]

Fala Daniel !

Quando tento fazer dessa forma que passou da erro no momento de inerir o pedidoItem, pois diz que o "IdPedido" esta nulo. Veja o erro abaixo:

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'idPedido' cannot be null
Error Code: 1048
Call: INSERT INTO pedidoItem (Id, Item, idPedido) VALUES (?, ?, ?)
        bind =&gt; [3, item1, null]
Query: InsertObjectQuery(3)
        at oracle.toplink.essentials.internal.ejb.cmp3.transaction.base.EntityTransactionImpl.commit(EntityTransactionImpl.java:105)
        at oracle.toplink.essentials.internal.ejb.cmp3.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:45)
        at exemplotoplinkrelacionamento.Main.persist(Main.java:50)
        at exemplotoplinkrelacionamento.Main.main(Main.java:31)

Você faz assim???

pedido.getPedidosItem.add( itemPedido ); itemPedido.setPedido( pedido ); da.save( pedido );

Não !
Estou fazendo assim:

pedido.getPedidosItem.add( itemPedido ); da.save( pedido );[/quote]
Não estou fazendo set do pedido no pedidoItem.
Se fizer da forma que vc disse funciona. Mas o correto não é o TopLink fazer isso sozinho ?

Não existe mágica. Você tem de relacionar o ItemPedido ao Pedido. O Toplink trata de persistir isso pra você, desde que corretamente relacionado.

Então a anotação cascade=CascadeType.ALL (que ALL seria: Merge, Persist, Refresh, Remove) não serve para nada ?

Bom, levando em conta o que vc disse que “não existe mágica”; Se eu fosse apagar um pedido então, eu teria que passar o idPedido manualmente para o pedidoItem conseguir ser excluido certo ?

Porém, se faço remove(Pedido); o mesmo remove sem eu ter que passar o idPedido para o PedidoItem. Isso seria mágina então ? ou seria o cascade.CascadeType.REMOVE que estaria conseguindo passar o idPedido para PedidoItem e remover os dois ?

Portanto ao meu ver (posso estar errado), isso é um problema do TopLink !
Postei aqui, para realmente ver se isso não estava funcionando com outras pessoas também.

Obs.: Este procedimento funciona normalmente no Hibernate (sem eu precisar setar o pedido para o pedidoItem), não estou utilizando ele devido a outras coisas.

Grato !

O Cascade serve para definir o tipo de ações em cascata que ele deve aplicar nos relacionamentos.

Se você não define o relacionamento correto entre os Objetos (e não estou falando em Hibernate/Toplink), os frameworks ORM não tem como ter a referência aos objetos envolvidos. Por isso não há mágica.

Quando você faz:

Pedido p = dao.buscarPedido( id ); dao.remove( p );

Se for Cascade All, ele deve deletar os itens do pedido, por que você configurou assim.

Mas se você faz:

Pedido p = new Pedido(); ItemPedido i1 = new ItemPedido(); dao.save( p ); dao.save (i1);

O framework não tem como saber como eles se relacionam.

Cada implementação de ORm trabalha de uma forma diferente, pode ser que o Hibernate deduza alguns relacionamentos e o Toplink não. Por isso, o JPA é uma utopia no que diz respeito a interoperabilidade entre diferentes ORMs.

Certo, entendi.

Vlw !

Seguinte Daniel e Pessoal !

Efetuei alguns testes aqui, e cheguei as seguintes conclusões:

- Usando mapeamento Bidirecional:
Deu certo somento quando realizei o procedimento (abaixo) que vc disse, ou seja, ligando os dois lados dos objetos. Sendo assim seto o pedido para o pedidoItem:

pedido.getPedidosItem.add( itemPedido );  
itemPedido.setPedido( pedido );  
da.save( pedido );  

- Usando mapeamento Unidirecional:
Deu certo da maneira (abaixo) como eu falei, ou seja, configurando apenas um lado (Pedido). Sendo assim não seto o pedido para o pedidoItem:

pedido.getPedidosItem.add( itemPedido );
da.save( pedido );  

Porém, o TopLink cria a seguinte estrutura “estranha” de tabelas:

pedido
pedidoItem
pedido_pedidoItem.

Como já disse acima, neste caso funciona perfeitamente também, pois a tabela de ligação é populada como se fosse um relacionamento NxN.

- Minha dúvida é a seguinte:
Eu tenho um modelo ER legado, onde no relacionamento “Pedido - PedidoItem” tem uma chave estrangeira de pedido para a tabela PedidoItem.
Como fazer isso no mapeamento Unidirecional :?: :?: :?:

Obs.: Fiz o seguinte, tentei adicionar a opção de @JoinColumn para o mapeamento Unidirecional, porém ele me adverte que não posso usar dessa forma, preciso mudar para @JoinTable.

Obs. 2: Acho que não tem jeito mesmo. Veja o link: https://glassfish.dev.java.net/issues/show_bug.cgi?id=2049

Grato !

Eu acredito que você resolve isso com o CascadeType.PERSIST
pois persistindo o pedido, são persistidos os itens.

Em relação a isso, vc deve relaxar.
A forma como ele cria as tabelas pouco importa para nós. Estamos preocupados com os objetos!

Pelo que eu entendi, você tem duas classes.

  1. Pedido
  2. PedidoItem

Para um relacionamento Unidirecional, a classe Pedido não deve ter atributo da classe PedidoItem;
Já a segunda classe deve ter um atributo Pedido. Assim por exemplo:

@Entity
public class PedidoItem implements Serializable {

  @Id
  private Integer id;
  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumn(name="PEDIDO_ID",nullable=false)
  private Pedido pedido;

}

Lembrando que eu uso o EclipseLink como implementador da especificação JPA.
E também tem uma coisa muito louca que aconteceu comigo.
Eu estava tendo este mesmo problema que você (da chave estrangeira passada como null)
Eu resolvi declarando o parâmetro ‘pedido’ por último e colocando-o como último na criação do objeto, com new();
que estranho né?