Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

FIND e PERSIST vs MERGE

Olá pessoal, eu gostaria de esclarecer umas dúvidas...

Após o instrutor mostrar o merge e a criação de um novo entitiy manager, tentei atualizar os dados no banco de dados automaticamente igual acontecia nos EMs anteriores (quando foi utilizado find e persist) apenas utilizando o setter e as transações.

Pela maneira que o instrutor disse, o find e persist deixam o nosso objeto no estado managed, e o merge também fazia isso.

Logo, eu havia concluído que bastava fazer algo do tipo novoEM.merge(MeuObjeto) e depois voltar a fazer as alterações que eu quisesse via setter e transações que tudo ia voltar a funcionar. Como antes.

Mas não foi o caso. Esse comportamento só voltaria a acontecer se eu buscasse o objeto a partir do método find, ou então, eu iria acabar caindo na contradição que o instrutor não queria que caísse: Que o merge é para update.

Existe mesmo essa situação? Mesmo que um objeto seja managed, ele nao está digamos que... Totalmente sincronizado com o Hibernate? (no quesito de suas alterações). De qualquer forma, acredito que o instrutor poderia ter comentado sobre isso, uma vez que ele não quisesse que nós caíssemos no dilema do update.

Vou mostrar os cases aqui: 1) Assim funciona do jeito que eu esperava (utilizando o find)

//Com o em1 fechado, hora de fazer atualizações em 'leonardo' e depois 'mergear';
        leonardo = em2.find(Conta.class, 1L);
        leonardo.setSaldo(1234.0);

        //Tentando alteração
        em2.getTransaction().begin();
        em2.getTransaction().commit();

        em2.close();

2) Assim funciona, mas NÃO do jeito que eu esperava (utilizando o merge como update)

        //Com o em1 fechado, hora de fazer atualizações em 'leonardo' e depois 'mergear';
        leonardo.setSaldo(55554.0);
        em2.merge(leonardo);
        //Tentando alteração
        em2.getTransaction().begin();
        em2.getTransaction().commit();

        em2.close();

3) Assim não funciona, porém foi uma possibilidade que eu havia entendido

        //Com o em1 fechado, hora de fazer atualizações em 'leonardo' e
         // eventualmente salvar as alterações
        em2.merge(leonardo);
        leonardo.setSaldo(12345.0);

        //Tentando alteração
        em2.getTransaction().begin();
        em2.getTransaction().commit();

        em2.close();
2 respostas
solução!

Olá Arthur, tudo bem com você?

Você poderia fornecer a primeira parte do código também? Digo isso pois não sei se está sendo criada uma nova instancia de leonardo toda vez, ou apenas em alguns casos.

  1. Assumindo que no primeiro caso o leonardo que é persistido no banco é o mesmo que tem o id 1L. Então tudo bem, após usar o find() ele fica no estado managed e seu saldo é alterado sem problemas depois do commit();

  2. Nesse caso, vai depender de quem é o leonardo. Em outras palavras, nesse contexto leonardo foi instanciado ou foi recuperado através do find()?

    Caso ele tenha sido instanciado antes no código, o update realmente não vai funcionar da maneira como esperamos. Já que ele vai inserir esse novo leonardo no banco com em.persist(leonardo), depois checar por alterações e realizar o update do saldo no commit(). Agora, se o leonardo foi recuperado do banco com Conta leonardo = em.find(Conta.class, 1L), o update vai ocorrer normalmente já que agora nós temos uma cópia real do leonardo do banco e não uma nova instância dele.

  3. Essa parte eu confesso que pode ser a mais confusa e envolve o que o merge() de fato faz com o objeto. O que pode ser confuso, pois os comandos sql que ele pode gerar dependem do estado em que o objeto se encontra. Nesse caso, sabemos que o leonardo está no modo detached já que o EntityManager em foi fechado, portanto o que acontece ao passarmos o leonardo para o merge() é que uma nova cópia do leonardo é retornado no estado managed.

    Em outras palavras, quando usamos o leonardo.setSaldo(12345.0), estamos mudando o saldo do leonardo que está no estado detached. Enquanto que a cópia managed de leonardo que foi retornada nunca mais é tocada.

Aqui eu toquei apenas em alguns pontos que achei importante, mas posso ter deixado alguns detalhes relevantes de fora. Portanto eu recomendo dar uma lida nesse artigo que detalha um pouco mais do funcionamento do merge().

Em relação à fala do instrutor, o perigo está em associar um comando do JPA a um comando SQL específico, sendo que um comando do JPA pode desencadear múltiplos comandos SQL diferentes dependendo do contexto.

Peço desculpas se estendi muito a explicação ou ela não tenha ficado clara, mas também peço que volte aqui caso algum ponto não faça sentido!

Abraços e bons estudos!!

Oi Thiago. O problema era exatamente este mesmo. Eu li o artigo do Paulo Silveira aqui https://blog.caelum.com.br/entidades-managed-transient-e-detached-no-hibernate-e-jpa/ e resolveu a dúvida. Na formação antiga de Java, ele fazia parte da grade curricular. Já nesta nova, (Spring Framework) não faz. Seria interessante se ele fosse adicionado mais uma vez.

De qualquer forma, obrigado pela a sua resposta. Conhecimento nunca é demais.