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

Relato de experiência

Olá a todos, tudo certo?
Gostaria de relatar uma experiência que fiz para fins de teste.
Sabemos que, uma entidade no estado transient é aquela que ainda não foi mapeada em um registro de uma tabela, ou seja, é um objeto recém criado em memória, que não tem um id setado.
O que fiz foi o seguinte:
  • Criei uma entidade chamada Pessoa, com os atributos id do tipo Integer, nome do tipo String, idade do tipo int e sexo do tipo String.
  • Setei o id como auto_increment através da anotação @GeneratedValue
  • Criei uma classe com o método main
  • Dentro do método main, criei um entity manager e uma pessoa, no estado transient.
  • Em vez de chamar o método persist do entity manager para deixar a pessoa managed, resolvi chamar o método merge na pessoa, ainda transient.
  • Depois do commit da transação, imprimi no console o id da pessoa, que veio nulo. Porém, a pessoa foi inserida no banco.
  • Depois disso, fechei o entity manager e mudei o nome da pessoa. Abri um novo entity manager e chamei o método merge denovo.
  • Como a pessoa ainda estava transient, ele salvou a pessoa denovo no banco, agora com o nome diferente. No entanto, o hibernate fez um insert into na segunda vez, assim como havia feito na primeira vez.
Conclusão: Se chamarmos o método merge em uma entidade transient, a JPA vai salvar essa entidade no banco, porém o id do objeto da entidade em memória continuará nulo, o que sugere que esta entidade continue transient. Se chamar novamente o merge, A JPA salva novamente a mesma entidade. Diferentemente de uma entidade detasched, onde quando chamamos o merge, é feito um select no banco para comparar os valores obtidos no registro da tabela com os valores dos atributos do objeto em memória. Gostaria de debater sobre esses resultados, se possível. Agradeço a atenção de todos, e aguardo. Abraços!
2 respostas
solução!

Olá novamente Guido , tudo bom?

Segundo o javadoc da classe EntityManager(https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html)  , temos para o método persist:

void persist(Object entity)
Make an instance managed and persistent.
Parameters:
entity - entity instance

Tora uma instância(parâmetro ) managed e persistente
e não possui nenhum retorno(void)

E no caso do método merge temos:

 T merge(T entity)
Merge the state of the given entity into the current persistence context.
Parameters:
entity - entity instance
Returns:
the managed instance that the state was merged to

Faz o merge do estado do objeto com o banco de dados e retorna uma instância gerenciada do objeto.

Diante dissso no passo do seu teste :

"Em vez de chamar o método persist do entity manager para deixar a pessoa managed, resolvi chamar o método merge na pessoa, ainda transient."

entity = entityManager.merge(entity);

e verifica

"Depois do commit da transação, imprimi no console o id da pessoa, que veio nulo. Porém, a pessoa foi inserida no banco."

Acredito que após isso o id venha preenchido.

Oi Breno, tudo certo?
Você tem rasão. 
O método merge do entity manager retorna uma nova instância do objeto da entidade, ou seja, ele retorna uma nova referência para um outro objeto com os mesmos valores do objeto passado como argumento no método. Para descobrir isso, além de olhar na documentação como você fez, imprimi os dois objetos, tanto a entidade pessoa antes de fazer o merge, quanto a referência à nova pessoa retornada pelo merge. 
Fiz isso sem sobreescrever o método toString, só para comparar os endereços de memória, e são diferentes. A pessoa transient, tem um endereço x, enquanto que a pessoa managed retornada pelo método merge, tem outro endereço de memória, por isso são referências diferentes, apontando para objetos diferentes. 
Pelo menos foi assim que compreendi. Não sei se compreendi corretamente. Mas é isso aí.
E realmente, o id veio preenchido no objeto em estado managed retornado pelo método merge.
Agradeço pela contribuição.
Valeu mesmo.
Abraços!