[RESOLVIDO] Erro de inserção no Hibernate  XML
Índice dos Fóruns » Persistência: Hibernate, JPA, JDBC e outros
Autor Mensagem
Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

Bom dia galera.

Estou passando por um problema usando JPA (através do Hibernate) .
Procurei por aí mas não achei nada que pudesse me ajudar, uma vez que é difícil encontrar tags para esse prob.
Enfim, esclarecerei o cenário antes de expor o problema.

Supondo que eu tenha as seguintes entidades(ps: todas são serializaveis):


OK, beleza.
Se eu fizer a persistencia de A que contenha uma coleção de B funciona perfeitamente.

PORRÉÉÉÉÉÉMM cai em uma situação onde o analista de banco decidiu que eu teria uma outra entidade C que possui uma chave composta para a entidade de relacionamento das entidades acima, a A_B.
Como estou usando JPA fui obrigado a mapear também essa entidade de relacionamento, ficando assim:



Bom, esse mapeamento funcionou também nos cenários:

1- Quando eu faço a persistencia de A e B ele persiste perfeitamente.
2- Quando persisto C onde o relacionamento A e B já exista, ele também funciona


PORÉM quando persisto no mesmo método os registros de A e B, criando automaticamente o registro na tabela de relacionamento A_B e vou persistir C da sempre um erro dizendo que não existe o relacionamento A_B, porém acabei de cria-lo logo acima no mesmo método.

Compreendem?
Agora se eu fizer essa persistência em chamadas separadas, tudo funciona perfeitamente!

Lembrando que estou usando Hibernate 3.5, Glassfish V3, EJB 3.x, JDK 1.6_u20, Fedora.... e postgres 8.4.

Já tentei usar o em.flush() logo depois de criar o relacionamento A_B e antes de tentar persistir C. ('em' é meu entitymanager injetado pelo conteiner jee no meu stateless session bean);


O que to entendendo é que quando crio o relacionamento A_B ele fica no cache do hibernate (na minha transação) e ao tentar finalizar o método ele executa as querys de persistencia de C mas que dependem de A_B, daí a dita Exception.

Não questionem os relacionamentos, foi uma decisão dos analistas de banco e faz todo o sentido no contexto em que estamos inseridos. Preferi não postar o código por questões de privacidade da empresa, não seria ético exibi-los por aqui (não nesse contexto).

Help?

Obrigado pessoal!

This message was edited 1 time. Last update was at 17/08/2010 15:28:42

Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

Então galera, tava dando uma olhada nas exceptions que isso ta me mandando e notei o seguinte trexo ANTES da mensagem que reclama que o registro da entidade A_B não existe:



Por que isso? Será que é isso que ta dando o erro de que o registro não existe? Mas ainda assim não tenho idéia do que possa estar gerando essa exception.

Abraços!
evefuji
GUJ Ranger

Membro desde: 14/12/2007 22:37:17
Mensagens: 753
Online

num sei se já tentou isso, mas ao terminar de inserir o relacionamento, tenta dar um commit.

E verifica se as chamadas ao banco, quando na mesma camada, não ocorrem em threads diferentes. Se ocorrem em threads diferentes pode ser que o do relacionamento em C possa estar pedindo por um registro que ainda não foi "commitado" em A_B
Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

evefuji wrote:num sei se já tentou isso, mas ao terminar de inserir o relacionamento, tenta dar um commit.

E verifica se as chamadas ao banco, quando na mesma camada, não ocorrem em threads diferentes. Se ocorrem em threads diferentes pode ser que o do relacionamento em C possa estar pedindo por um registro que ainda não foi "commitado" em A_B


Então, mas preu conseguir dar um commit, preciso fazer o seguinte:


Fiz isso já e não funcionou, além de que se eu der o getTransaction() ele lança uma IllegalArgumentException dizendo que em transações gerenciadas pelo conteiner eu não posso mexer na transação.

E como seriam essas Threads diferentes?
Eu persisto o relacionamento A_B antes de C dentro do mesmo método e pelo mesmo EntityManager.
Realmente, a impressão que tenho é que ele tenta executar os inserts de C antes de A_B.
O curioso é que no console ele exibe os inserts na ordem certinha....

Enfim, estou ficando sem esperanças....

Obrigado!
Andre Brito
JWizard

Membro desde: 21/07/2007 17:44:31
Mensagens: 2485
Localização: Paraná
Offline

Tchello,
Só por desencargo, se você atualizar A_B e tentar inserir C apontando para a relação atualizada, acontece a mesma coisa (acho que não vai lançar Exception, mas C vai ficar obsoleto)?

Outra coisa, você lida com alguma coisa de cache dentro do seu persistence.xml? Não me lembro no Hibernate, mas sei que no Eclipselink tem algumas configurações de cache.

Lembrei de alguma coisa que tinha lido na documentação do Hibernate:

3.10.1. In a transaction

Flush occurs by default (this is Hibernate specific and not defined by the specification) at the following points:
* before query execution (*)
* from javax.persistence.EntityTransaction.commit() (*)
* when EntityManager.flush() is called (*)
(*)* if a transaction is active

The SQL statements are issued in the following order
* all entity insertions, in the same order the corresponding objects were saved using EntityManager.persist()
* all entity updates
* all collection deletions
* all collection element deletions, updates and insertions
* all collection insertions
* all entity deletions, in the same order the corresponding objects were deleted using EntityManager.remove()


Referência.

Será que pode ter alguma relação com o seu problema?

Aliás, só mais uma coisa. Você falou que já tentou usar o flush e não deu certo, né? E tentou usar o refresh, mandando A_B?

This message was edited 1 time. Last update was at 16/08/2010 19:55:16


Como organizar o GUJ.
Meu Twitter.
Meu blog.
Future proofing means making code easy to change, not trying to anticipate every possible way your code might need to change.
[WWW]
Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

Andre Brito wrote:Tchello,
Só por desencargo, se você atualizar A_B e tentar inserir C apontando para a relação atualizada, acontece a mesma coisa (acho que não vai lançar Exception, mas C vai ficar obsoleto)?

Então, não entendi muito bem o que vc quis dizer, mas vamos la.
Fiz o seguinte:



Bom, esse trecho descreve muito bem o cenário que tenho.
Uma observação relevante a ser feita é que se eu executar isso em dois passos (duas chamadas distintas, não encadeadas) onde é feita a persistencia em separado tudo funciona perfeitamente.

Andre Brito wrote:
Outra coisa, você lida com alguma coisa de cache dentro do seu persistence.xml? Não me lembro no Hibernate, mas sei que no Eclipselink tem algumas configurações de cache.

Então, somente o padrão do hibernate.
Tentei mudar alguns argumentos mas sem sucesso.
Amanhã logo cedo postarei meus argumentos aqui.

Andre Brito wrote:
Lembrei de alguma coisa que tinha lido na documentação do Hibernate:

3.10.1. In a transaction

Flush occurs by default (this is Hibernate specific and not defined by the specification) at the following points:
* before query execution (*)
* from javax.persistence.EntityTransaction.commit() (*)
* when EntityManager.flush() is called (*)
(*)* if a transaction is active

The SQL statements are issued in the following order
* all entity insertions, in the same order the corresponding objects were saved using EntityManager.persist()
* all entity updates
* all collection deletions
* all collection element deletions, updates and insertions
* all collection insertions
* all entity deletions, in the same order the corresponding objects were deleted using EntityManager.remove()


Referência.

Será que pode ter alguma relação com o seu problema?


Essa informação é valiosíssima!
Seguindo esse raciocínio, se bem o entendi, meu problema está sendo causado por que a persistência de A_B está sendo feita após o C, uma vez que envolve collections, que por essa lógica é executado apenas após entidades comuns. Seria isso?
Se for, como posso garantir que o passo 1 seja executado antes?

Andre Brito wrote:
Aliás, só mais uma coisa. Você falou que já tentou usar o flush e não deu certo, né? E tentou usar o refresh, mandando A_B?

Na verdade não tentei. Um EntityManager regular possui esse método de refresh? (tem sim, encontrei no javadoc).
Lembrando que estou usando um EntityManager padrão do JPA injetado pelo conteiner, não o HibernateEntityManager, uma vez que não posso (nem tenho permissão) de atrelar esse código ao Hibernate (já que é desejo do cliente alternar para eclipselink, por exemplo, quando lhe for conveniente).

Pelo que li sobre o refresh, ele vai atualizar com o que está na base. O inverso também é valido, ele atualizará a base conforme a entidade caso esta não tenha sido persistida? E o controle de transações, não ficará afetado?

Amanhã assim que possível farei os testes com essa técnica.
Posto os resultados por aqui.

De qualquer maneira até lá qualquer ajuda, dica ou palpite pode ser de grande ajuda, já que acelera os testes que poderei executar.

Bom, creio que já tenhamos avançado bastante no modelo do cenário em que estou passando.
Agradeço imensamente a atenção dada ao meu tópico.

Abraços.

This message was edited 1 time. Last update was at 16/08/2010 20:32:48

Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

Bom dia!

Conforme prometido, seguem os argumentos:


Já tentei altera-los de tudo quanto é maneira, mas infelizmente sem sucesso.


Testei o refresh, tanto após a inserção de A, quando em A_B após a sua recuperação.
O erro persiste =/ ou seja, ele não persiste (entendeu a piada? hehehehehehehe).

Queria uma forma de garantir que o passo 1 fosse executado no banco antes dos passos seguintes, pelo visto é isso que está acontecendo (o inverso) =/

Aguardo ansiosamente por respostas.

Abraços!
Tchello
GUJ Master
[Avatar]

Membro desde: 07/06/2008 14:41:04
Mensagens: 1694
Offline

Caros, encontrei a razão do problema.
No mapeamento com duas JoinColumns o hibernate estava gerando as FKs invertidas.
Vou explicar.

O correto seria gerar algo assim:



Mas por algum motivo ele estava gerando assim:


De qualquer maneira isso não mostrará um problema em produção, uma vez que jamais permitimos geração automática do hibernate nesse ambiente.
Portando a solução foi gerar esse relacionamento manualmente, fazendo tudo funcionar perfeitamente.

Analisarei melhor o caso, ao que tudo indica o relacionamento foi mapeado corretamente, porém o hibernate fez a geração erroneamente. Caso isso se confirme abrirei um issue no Hibernate e posto o código aqui.

Agradeço imensamente a atenção de todos e espero que isso possa contribuir para outros usuários no futuro.

Abraços!

This message was edited 2 times. Last update was at 17/08/2010 15:28:00

 
Índice dos Fóruns » Persistência: Hibernate, JPA, JDBC e outros
Ir para:   
Powered by JForum 2.1.8 © JForum Team