terça-feira, 31 de maio de 2011

Problema com equals em entidades usando Hibernate

Há uns meses atrás eu tive um problema com uma combo e motivo do problema era a falta de um equals na classe. As vezes quando a página era renderizada o objeto que estava selecionado era de uma instancia diferente do correspondente da lista e com isso dava inconsistência.

Por este motivo todas as classes precisam do equals e hashcode implementado e o eclipse gera eles automaticamente. Mas ontem eu tive um problema com este equals gerado de maneira automática, pois ele gera o equals dessa maneira:


@Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  UsuarioLogin other = (UsuarioLogin) obj;
  if (idUsuario != other.idUsuario)
   return false;
  return true;
 }


Este equals pode gerar dois problemas em nosso ambiente por conta de como o hibernate trabalha. O hibernate cria constantemente classes em tempo de execução que herdam das classes da entidade e adicionam métodos que ele precisa. Vocês que trabalharam com Hibernate já devem ter visto Objetos com classes estranhas como br.com.infox.cliente.entity.Pessoa_$$_javassist_26.

O primeiro problema é que como são classes diferentes esta comparação sempre retorna false: if (getClass() != obj.getClass()). Para resolver deve-se testar com instanceof.

O segundo problema é o acesso aos campo da entidade. As vezes mesmo campos que não são lazy, só são populados quando são chamados pelo get do campo, então a comparação (idProcesso != other.idProcesso) pode retornar false, pois o valor pode não estar populado ainda.

Eu tive esse dois problemas ontem com a classe UsuarioLogin, e perdi um bom tempo para descobri isso.

Para tornar nosso equals das entidades compatíveis com o hibernate podemos reescrever ele assim:




@Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (!(obj instanceof UsuarioLogin))
   return false;
  UsuarioLogin other = (UsuarioLogin) obj;
  if (getIdUsuario() != other.getIdUsuario())
   return false;
  return true;
 }

Maiores explicações sobre estes problemas podem ser lidas aqui: http://blog.xebia.com/2008/03/advanced-hibernate-proxy-pitfalls/

Nenhum comentário: