O JPA trouxe muitos benefícios para os desenvolvedores Java, mas existe certa dificuldade em entender alguns detalhes desta especificação.
Para facilitar esta compreensão, vamos aqui analisar um método que gera muita controversa, que é o merge
.
Curso Amazon Web Services (AWS) - Elastic Beanstalk - Fundamentos
Conhecer o cursoEstados de um objeto JPA
Antes de vermos o método merge
em detalhes, é importante entendermos os estados das entidades JPA, pois estão diretamente relacionados com a função deste método.
No JPA um objeto pode assumir quatro estados: New, Managed, Removed e Detached. A imagem abaixo ilustra o ciclo de vida de um objeto que passa por esses quatro estados:
Quando um objeto é criado, ele se encontra no estado New. Neste estado, o objeto não tem nenhuma relação com EntityManager
ou uma representação
no banco de dados. Qualquer alteração no objeto, neste estado, não é notada pelo JPA.
Quando o objeto é persistido (geralmente em um banco de dados), ele passa para o estado Managed. Neste, qualquer alteração no objeto será sincronizada com o dado persistido. Essa sincronização não é em tempo real. Uma alteração no objeto não irá executar automaticamente um comando SQL, isso só irá acontecer em “flush time”.
O “flush time” ocorre por padrão (de acordo com a implementação do Hibernate, uma vez que a especificação do JPA não define isso) em três momentos:
- Antes da execução de uma query.
- Na chamada do método
commit()
deEntityTransaction
. - Na chamada do método
flush()
deEntityManager
.
É muito comum o objeto se manter por pouco tempo no estado Managed, pois, geralmente, após persistí-lo, o EntityManager
já é fechado e então ele passa para o estado Detached.
O estado Detached significa que o objeto não está vinculado ao EntityManager
. Qualquer alteração não irá impactar no dado persistido. Isso só irá ocorrer se o estado voltar a ser Managed.
Por fim, temos o estado Removed. O objeto assume este estado quando um objeto Managed é marcado para a remoção com o método remove
.
Funcionamento do merge
Agora que conhecemos os estados de um objeto JPA, é possível afirmar que ele só pode ser alterado, quando este está no estado Managed. Mas como
definir um objeto neste estado? Bom, isso pode ser feito através de dois métodos: persist
e merge
.
O funcionamento do persist
é bem simples: ao ser chamado, o objeto é salvo no banco e o seu estado muda para Managed.
Já o funcionamento do merge
irá depender de alguns fatores. O seu comportamento principal é lidar com objetos no estado Detached. Quando um objeto neste estado é passado para o método merge
, é retornado um novo objeto no estado Managed.
Se o objeto estiver no estado New, mas tendo os seus dados exatamente iguais aos persistidos na base, o merge
retornará um novo objeto no estado Managed. Mas, se o objeto no estado New contiver ao menos um dado diferente dos persistidos, será gerado um novo registro e, um novo objeto no estado Managed será retornado.
Se o objeto contiver outros objetos relacionados (através de uma propriedade de relacionamento) e o JPA não conseguir localizar seus dados no banco, também será gerado um novo registro do objeto e também dos que estiverem relacionados. Além disso, um novo objeto no estado Managed também será retornado.
Se o objeto estiver no estado Removed o método merge
irá gerar um erro.
Repare que sempre cito que o merge
retorna o objeto no estado Managed. Assim, o uso correto desse método é:
Entidade e = new Entidade();
Entidade e2 = em.merge(e);
e2.setAtributo(novoValor);
Veja que penas o objeto e2
se encontra no estado Managed. Qualquer alteração em e
não será sincronizada com o banco. Se o e
fosse alterado, como abaixo:
Entidade e = new Entidade();
em.merge(e);
e.setAtributo(novoValor);
Ao executar o “flush”, a alteração não seria refletida no banco de dados.
Caso não tenha certeza se o método merge
irá gerar um novo registro ou não, opte pelo uso do find
:
Entidade e = em.find(Entidade.class, 1);
Este método sempre irá retornar um objeto no estado Managed (ou null
se o registro não for encontrado). Aí não corremos o risco de gerar um novo registro apenas para se obter um objeto neste estado.