Na última semana descobri um problema (pra mim é um bug) no Hibernate3 que me deixou muito confuso por algumas horas.Eu tenho uma entidade do Hibernate chamada Pessoa, que é usada em vários outros sistemas (afinal, todos os sistemas possuem alguma associação com pessoas).
Tudo estava funcionando perfeitamente, até que surgiu a idéia de implementar versioning for optimistic locking nesta classe (se você não conhece como funciona o versionamento do Hibernate, clique aqui). Foi muito simples, apenas adicionei @Version em um atributo do tipo Date e as instâncias ficaram protegidas de alterações concorrentes.
O problema é que, depois dessa alteração, todos os sistemas começaram apresentar problemas em telas variadas. A solução (depois de quebrar muito a cabeça) foi não usar versionamento para esta entidade. Eu sei que na verdade não foi uma solução, mas resolveu o problema. E nessas horas o difícil é imaginar que um simples @Version gerou tudo isso.
Para tentar exemplificar o problema, criei duas entidades chamadas Customer e Invoice, como seguem:
@Entity @Table public class Customer implements Serializable { @Id @GeneratedValue private Long id; private String name; @Version @Column(name="update_date") private Date updateDate; //getters and setters } @Entity @Table public class Invoice implements Serializable { @Id @GeneratedValue private Long id; private Integer number; private BigDecimal amount; //getters and setters }Veja que o atributo updateDate da classe Customer está anotada com @Version.
Criei um método com o seguinte código para testar o comportamento do Hibernate com @Version:
Session session = sessionFactory.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setId(1L); Invoice invoice = new Invoice(); invoice.setNumber(1234); invoice.setAmount(new BigDecimal(4500.19)); invoice.setCustomer(customer); session.merge(invoice); transaction.commit();Considere o customer de código 1 já cadastrado no banco de dados.
Agora tente adivinhar o resultado da execução desse código. Pra mim deveria funcionar, pois o merge() deveria tornar o meu objeto customer persistente, mas infelizmente é lançada a seguinte exceção:org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: example.CustomerO engraçado é que se incluirmos a seguinte linha depois de atribuir o código 1 ao id de customer, funciona:
customer.setUpdateDate(new Date(1, 1, 1, 1, 1, 1));Veja que eu defini uma data de alteração (que nem faz sentido) ao objeto, e agora funciona perfeitamente, gerando o seguinte SQL:
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.update_date as update3_0_0_ from Customer customer0_ where customer0_.id=? Hibernate: insert into Invoice (amount, customer_id, number) values (?, ?, ?)Se comentarmos o @Version do atributo updateDate da classe Customer e também a atribuição da data de alteração de Customer, funcionará corretamente também, pois aí não estaria mais utilizando versionamento.
Se quiser fazer outros testes, baixe do código-fonte do projeto de exemplo clicando aqui.
E pra você, isso é um bug? Será que a especificação do JPA contempla isso?
Assim que tiver tempo, irei traduzir esse post e cadastrar no JIRA do Hibernate.Abraços,
Fonte: http://www.infoblogs.com.br/view.action?contentId=21705
http://www.javafree.org/news/view.jf?idNew=3682
http://www.hibernate.org/hib_docs/annotations/reference/en/html_single/#entity-mapping-entity-version
