Bug no Optimistic Locking do Hibernate 3

0 respostas
Eduardo_Bregaida

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.Customer

O 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

:smiley:

Criado 14 de novembro de 2007
Respostas 0
Participantes 1