Domanda

Come devono essere implementati gli uguali e gli hashcode della classe del modello in Hibernate? Quali sono le insidie ??comuni? L'implementazione predefinita è abbastanza buona per la maggior parte dei casi? Ha senso usare le chiavi aziendali?

Mi sembra piuttosto difficile farlo funzionare bene in ogni situazione, quando vengono presi in considerazione il recupero pigro, la generazione di ID, il proxy, ecc.

È stato utile?

Soluzione

Hibernate ha una bella e lunga descrizione di quando / come sovrascrivere equals () / hashCode () in documentazione

L'essenziale è che devi preoccuparti solo se la tua entità farà parte di un Set o se stai per staccare / allegare le sue istanze. Quest'ultimo non è così comune. Il primo è generalmente gestito meglio tramite:

  1. Basare equals () / hashCode () su una chiave aziendale - ad es. una combinazione unica di attributi che non cambierà durante il ciclo di vita degli oggetti (o, almeno, della sessione).
  2. Se quanto sopra è impossibile, equals () / hashCode () sulla chiave primaria SE è impostato e identità oggetto / System.identityHashCode () altrimenti. La parte importante qui è che devi ricaricare il tuo Set dopo che la nuova entità è stata aggiunta e persistente; altrimenti potresti finire con uno strano comportamento (che alla fine si traduce in errori e / o corruzione dei dati) perché la tua entità potrebbe essere allocata a un bucket che non corrisponde al suo hashCode () corrente
  3. .

Altri suggerimenti

Non credo che la risposta accettata sia accurata.

Per rispondere alla domanda originale:

  

L'implementazione predefinita è abbastanza buona per la maggior parte dei casi?

La risposta è sì, nella maggior parte dei casi lo è.

Devi solo sostituire equals () e hashcode () se l'entità verrà utilizzata in un Set (che è molto comune ) E l'entità verrà staccata e successivamente ricollegata a sessioni di ibernazione (che è un uso non comune di ibernazione).

La risposta accettata indica che i metodi devono essere sostituiti se la condizione o è vera.

Quando un'entità viene caricata tramite caricamento lento, non è un'istanza del tipo base, ma è un sottotipo generato dinamicamente generato da javassist, quindi un controllo sullo stesso tipo di classe fallirà, quindi non usare:

if (getClass() != that.getClass()) return false;

invece usa:

if (!(otherObject instanceof Unit)) return false;

che è anche una buona pratica, come spiegato in L'implementazione dell'eguaglianza nelle Pratiche Java .

per lo stesso motivo, accedendo direttamente ai campi, potrebbe non funzionare e restituire null, invece del valore sottostante, quindi non usare il confronto sulle proprietà, ma usare i getter, poiché potrebbero innescarsi per caricare i valori sottostanti.

La migliore implementazione è uguale a / hashCode quando si utilizza un chiave di business univoca .

La chiave aziendale deve essere coerente in tutti gli stati transizioni (transitorio, allegato, separato, rimosso), ecco perché non puoi fare affidamento su id per l'uguaglianza.

Un'altra opzione è passare all'utilizzo di identificatori UUID , assegnati dall'applicazione logica. In questo modo, puoi usare l'UUID per equals / hashCode perché l'id viene assegnato prima che l'entità venga svuotata.

Puoi persino usare l'identificatore di entità per uguale a e hashCode , ma ciò richiede che tu restituisca sempre lo stesso valore hashCode in modo da assicurarsi che il valore hashCode dell'entità sia coerente in tutte le transizioni dello stato dell'entità. Dai un'occhiata a questo post per ulteriori informazioni su questo argomento .

Sì, è difficile. Nel mio progetto è uguale e hashCode si basano entrambi sull'id dell'oggetto. Il problema di questa soluzione è che nessuno dei due funziona se l'oggetto non è stato ancora persistito, poiché l'id viene generato dal database. Nel mio caso è tollerabile poiché in quasi tutti i casi gli oggetti sono persistenti immediatamente. Oltre a ciò, funziona benissimo ed è facile da implementare.

Se ti è capitato di sostituire uguale a , assicurati di adempiere ai suoi contratti: -

  • SIMMETRIA
  • RIFLETTENTE
  • TRANSITIVO
  • COERENTE
  • NON NULL

E sovrascrive hashCode , poiché il suo contratto si basa sull'implementazione di uguale a .

Joshua Bloch (designer del framework Collection) ha fortemente raccomandato di seguire queste regole.

  • elemento 9: sostituisci sempre hashCode quando esegui l'override uguale a

Ci sono gravi effetti indesiderati quando non si seguono questi contratti. Ad esempio List.contains (Object o) potrebbe restituire un valore booleano errato poiché il contratto generale non è stato rispettato.

Nella documentazione di Hibernate 5.2 afferma che potresti non voler implementare hashCode ed è uguale a tutti, a seconda della tua situazione.

https: // docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

Generalmente, due oggetti caricati dalla stessa sessione saranno uguali se sono uguali nel database (senza implementare hashCode e uguale).

Diventa complicato se stai usando due o più sessioni. In questo caso, l'uguaglianza di due oggetti dipende dall'implementazione del metodo uguale.

Inoltre, ti metterai nei guai se il tuo metodo uguale sta confrontando gli ID che vengono generati solo persistendo un oggetto per la prima volta. Potrebbero non esserci ancora quando viene chiamato equals.

C'è un articolo molto carino qui: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

Citando una riga importante dall'articolo:

  

Raccomandiamo di implementare equals () e hashCode () usando la chiave Business   uguaglianza. Uguaglianza chiave aziendale significa che il metodo equals ()   confronta solo le proprietà che formano la chiave aziendale, una chiave che   identificherebbe la nostra istanza nel mondo reale (un candidato naturale   chiave):

In termini semplici

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 
}

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top