2009-12-15 13 views
10

Durante la lettura della documentazione di Hibernate, continuo a vedere i riferimenti al concetto di un identificatore naturale .Che cos'è un identificatore naturale in Hibernate?

Questo significa solo l'id che un'entità ha a causa della natura dei dati che detiene?

E.g. Il nome utente + password + età + qualcosa sono usati come identificatore composto?

risposta

11

In Hibernate, le chiavi natrual vengono spesso utilizzate per le ricerche. Nella maggior parte dei casi si avrà un ID surrogato generato automaticamente. Ma questo ID è piuttosto inutile per le ricerche, poiché interrogerai sempre per campi come il nome, il numero di previdenza sociale o qualsiasi altra cosa del mondo reale.

Quando si utilizzano le funzionalità di memorizzazione nella cache di Hibernate, questa differenza è molto importante: se la cache viene indicizzata dalla chiave primaria (ID surrogato), non si otterranno guadagni di prestazioni nelle ricerche. Ecco perché è possibile definire un insieme di campi con cui interrogare il database: l'ID naturale. Hibernate può quindi indicizzare i dati con la tua chiave naturale e migliorare le prestazioni di ricerca.

Vedere questo eccellente blog post per una spiegazione più dettagliata o questo RedHat page per un file di mapping Hibernate di esempio.

8

Ciò che identifica naturalmente un'entità. Ad esempio, il mio indirizzo email.

Tuttavia, una stringa di lunghezza variabile lungo non è una chiave ideale, quindi si consiglia di definire un surrogate id

AKA Natural key nel design relazionale

2

Un numero di sicurezza sociale potrebbe essere un natural identity, o come si Ho detto un hash delle informazioni dell'utente. L'alternativa è una surrogate key, ad esempio un Guid/UID.

+0

L'hash (e non deve essere un hash dato che una chiave può essere multi-colonna) sarebbe una chiave naturale valida solo se i dati non possono essere modificati (la posta elettronica va bene, il nome è iffy, la password è improbabile e l'età è sbagliata). – Tordek

+0

@Chris S: Non di fronte: "surrogato" – gbn

+0

@Tordek: buon punto. @Gbn Ha aggiornato il testo un po '. Gli articoli di wikipedia in realtà hanno buone spiegazioni di entrambi –

5

Un identificatore naturale è qualcosa che viene utilizzato nel mondo reale come identificativo. Un esempio è un numero di previdenza sociale o un numero di passaporto.

In genere è una cattiva idea utilizzare gli identificatori naturali come chiavi in ​​un livello di persistenza perché a) possono essere modificati al di fuori del proprio controllo eb) possono finire per non essere univoci a causa di un errore altrove, e quindi il tuo modello di dati non può gestirlo in modo che la tua applicazione esploda.

+0

Si spera che la chiave sia vincolata, ad es. Vincolo di chiave primaria per ridurre tali rischi – gbn

+1

Puoi vincolarlo nel tuo modello di dati, ma non puoi limitare la vita reale - gli errori accadono, e il tuo modello di dati non ha bisogno di rompere quando lo fanno. Se è necessario correggere l'SSN di qualcuno perché, ad esempio, è stato inserito in modo errato, dovrebbe essere un singolo UPDATE. Se lo hai usato come chiave in tutto il tuo sistema ... serializzato, memorizzato in backup e forse persino inviato a sistemi esterni, sei completamente fregato. Non c'è modo che tu possa aggiornare l'SSN di quella persona senza rompere qualcosa. PS: non memorizzare affatto SSN a meno che non sia necessario. –

+1

È vero, ha ancora bisogno di restrizioni e ci dovrebbe essere una differenza tra il modello logico e l'implementazione. SSN non è nemmeno unico ... http://www.computerworld.com/s/article/300161/Not_So_Unique – gbn

1

In un sistema di database relazionale, in genere, si può avere two types of simple identifiers:

  • chiavi naturali, che sono assegnati da sistemi esterni e garantito per essere unico
  • chiavi surrogate, come IDENTITY or SEQUENCE che sono assegnati dal Banca dati.

Il motivo per cui chiavi surrogate sono così popolari è che sono più compatti (4 byte o 8 byte), a fronte di una chiave naturale, che è molto lungo (ad esempio, il VIN prende 17 caratteri alfanumerici, il libro ISBN è 13 cifre a lungo).

Ora, se la chiave surrogata diventa la chiave primaria, si può usare l'annotazione JPA @Id.

E, se si dispone di un'entità che ha anche una chiave naturale, oltre a quella surrogata, è possibile map it with the Hibernate-specific @NaturalId annotation:

@Entity(name = "Post") 
@Table(name = "post") 
public class Post { 

    @Id 
    @GeneratedValue 
    private Long id; 

    private String title; 

    @NaturalId 
    @Column(nullable = false, unique = true) 
    private String slug; 

    //Getters and setters omitted for brevity 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) 
      return false; 
     Post post = (Post) o; 
     return Objects.equals(slug, post.slug); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(slug); 
    } 
} 

Ora, considerando l'entità sopra, l'utente potrebbe aver segnalibro un articolo Post e ora vogliono leggerlo. Tuttavia, l'URL con segnalibro contiene l'identificatore naturale slug, non la chiave primaria.

Quindi, siamo in grado di recuperare in questo modo usando Hibernate:

Post post = entityManager.unwrap(Session.class) 
.bySimpleNaturalId(Post.class) 
.load(slug); 

E Hibernate eseguire i seguenti due query:

SELECT p.id AS id1_0_ 
FROM post p 
WHERE p.slug = 'high-performance-java-persistence' 

SELECT p.id AS id1_0_0_, 
     p.slug AS slug2_0_0_, 
     p.title AS title3_0_0_ 
FROM post p 
WHERE p.id = 1 

La prima query è necessaria per risolvere l'identificatore di entità associata con l'identificatore naturale fornito.

La seconda query è facoltativa se l'entità è già caricata nella cache del primo o del secondo livello.

Come ho spiegato in this article, il motivo per cui si ha la prima query è perché Hibernate ha già una logica consolidata per caricare e associare le entità tramite il loro identificatore nel contesto di persistenza.

Ora, se si desidera saltare la query identificativo entità, si può facilmente annotare l'entità utilizzando il @NaturalIdCache della nota:

@Entity(name = "Post") 
@Table(name = "post") 
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE 
) 
@NaturalIdCache 
public class Post { 

    @Id 
    @GeneratedValue 
    private Long id; 

    private String title; 

    @NaturalId 
    @Column(nullable = false, unique = true) 
    private String slug; 

    //Getters and setters omitted for brevity 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) 
      return false; 
     Post post = (Post) o; 
     return Objects.equals(slug, post.slug); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(slug); 
    } 
} 

In questo modo, è possibile recuperare l'entità Post senza nemmeno colpire il database. Fantastico, giusto?

Problemi correlati