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?
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
@Chris S: Non di fronte: "surrogato" – gbn
@Tordek: buon punto. @Gbn Ha aggiornato il testo un po '. Gli articoli di wikipedia in realtà hanno buone spiegazioni di entrambi –