Como deve iguais e hashcode ser implementado pelo uso da JPA e Hibernate
Pergunta
Como deve modelar iguais de classe e hashcode ser implementado em hibernação? Quais são as armadilhas comuns? É o suficiente padrão implementação boa para a maioria dos casos? Existe algum sentido para usar teclas de negócios?
Parece-me que é muito difícil obtê-lo direito ao trabalho em cada situação, quando busca preguiçosa, geração de id, proxy, etc são levados em conta.
Solução
O Hibernate tem um bom e longo descrição de quando / como substituir equals()
/ hashCode()
em documentação
A essência do que é que você só precisa se preocupar com isso se a sua entidade será parte de um Set
ou se você estiver indo para retirar / colocar suas instâncias. Este último não é tão comum. O primeiro é geralmente melhor tratada através de:
- Basing
equals()
/hashCode()
em uma chave de negócios - por exemplo, uma combinação única de atributos que não vai mudar durante o objeto (ou, pelo menos, sessão) vida. - Se o acima é impossível,
equals()
base /hashCode()
na chave primária se é conjunto e objeto identidade /System.identityHashCode()
contrário. O importante parte aqui é que você precisa Recarregar o seu Set após nova entidade foi adicionado a ele e persistiu; caso contrário você pode acabar com um comportamento estranho (resultando em erros e / ou corrupção de dados) porque a sua entidade pode ser imputados a um balde que não corresponde a suahashCode()
atual.
Outras dicas
Eu não acho que a resposta aceita é preciso.
Para responder à pergunta original:
tem boa o suficiente implementação padrão para a maioria dos casos?
A resposta é sim, na maioria dos casos é.
Você só precisa equals()
override e hashcode()
se a entidade será usado em um Set
(que é muito comum) E a entidade vai ser separada e, posteriormente, re-conectado, hibernate sessões (que é um uso incomum de hibernação).
A resposta aceita indica que os métodos precisam ser anulado se qualquer condição é verdadeira.
Quando uma entidade é carregado através de carregamento lento, não é uma instância do tipo base, mas é um subtipo gerado dinamicamente gerado pelo Javassist, assim, um cheque no mesmo tipo de classe irá falhar, então não use:
if (getClass() != that.getClass()) return false;
utilizar:
if (!(otherObject instanceof Unit)) return false;
que também é uma boa prática, como explicado na Implementação iguais em Práticas de Java .
pela mesma razão, acessando diretamente campos, pode não funcionar e nulo retorno, em vez do valor subjacente, portanto, não use a comparação das propriedades, mas usar os getters, uma vez que pode desencadear para carregar os valores subjacentes.
A melhor implementação equals
/ hashCode
é quando você usa um chave única de negócios .
A chave de negócios deve ser consistente em todos estado entidade transições (transitória, anexado, individual, removido), é por isso que você não pode confiar em id para a igualdade.
Outra opção é mudar para usando UUID identificadores , atribuído pelo aplicativo lógica. Dessa forma, você pode usar o UUID para o equals
/ hashCode
porque o ID é atribuído antes de a entidade é liberada.
Você ainda pode usar o identificador de entidade para equals
e hashCode
, mas que exige que você retornar sempre o mesmo valor hashCode
para que você se certificar de que o valor entidade hashCode é consistente em todas as transições de estado entidade. Confira este post para saber mais sobre este tema .
Sim, é difícil. No meu projeto iguais e hashCode ambos contam com o id do objeto. O problema desta solução é que nenhum deles funciona se o objeto não foi persistiu, no entanto, como o ID é gerado pelo banco de dados. No meu caso que é tolerável uma vez que em quase todos os casos os objetos são persistentes imediatamente. Fora isso, ele funciona muito bem e é fácil de implementar.
Se você passou a substituir equals
, certifique-se de cumprir os seus contratos: -
- SYMMETRY
- REFLEXIVA
- transitivo
- CONSISTENTE
- Não NULL
E hashCode
override, como seu contrato dependem de implementação equals
.
Joshua Bloch (designer de quadro Collection) instou veementemente estas regras a serem seguidas.
- ponto 9: Sempre substituir hashCode quando você substituir iguais
Existem efeito não intencional sério quando você não seguir esses contratos. Por exemplo List.contains(Object o)
pode retornar valor boolean
errado como o contrato geral não cumpridas.
Na documentação do Hibernate 5.2 ele diz que você não pode querer implementar hashCode e iguais em tudo -. Dependendo da sua situação
Geralmente, dois objetos carregados a partir da mesma sessão será igual se eles são iguais no banco de dados (sem implementar hashCode e igual).
Fica complicado se você estiver usando duas ou mais sessões. Neste caso, a igualdade de dois objetos depende de sua implementação equals-método.
Além disso, você vai ter problemas se o seu equals-método é comparar IDs que só são gerados enquanto persistir um objeto pela primeira vez. Eles podem não estar lá mas quando iguais é chamado.
Não é muito bom artigo aqui: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Citando uma importante linha do artigo:
Recomendamos iguais de execução () e hashCode () usando a chave do negócio igualdade. -chave de negócios significa igualdade que o método () é igual a compara apenas as propriedades que formam a chave de negócios, uma chave que identificaria nossa instância no mundo real (um candidato natural chave):
Em termos simples
public class Cat {
...
public boolean equals(Object other) {
//Basic test / class cast
return this.catId==other.catId;
}
public int hashCode() {
int result;
return 3*this.catId; //any primenumber
}
}