Dans un système de base de données relationnelle, généralement, vous pouvez avoir two types of simple identifiers:
- clés naturelles, qui sont affectées par des systèmes externes et certifiée unique
- clés Surrogate, comme IDENTITY or SEQUENCE qui sont affectés par la base de données.
La raison pour laquelle les clés techniques sont si populaires est qu'ils sont plus compacts (4 octets ou 8 octets), par rapport à une clé naturelle qui est très longue (par exemple, le VIN prend 17 caractères alphanumériques, le livre ISBN est 13 chiffres longtemps). Maintenant, si la clé de substitution devient la clé primaire, vous l'utilisez en utilisant l'annotation JPA @Id
.
Et, si vous avez une entité qui dispose également d'une clé naturelle, en plus d'une Surrogate, vous pouvez 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);
}
}
Maintenant, compte tenu de l'entité ci-dessus, l'utilisateur peut bookmarked un article Post
et maintenant ils veulent le lire. Cependant, l'URL mise en signet contient l'identificateur naturel slug
, et non la clé primaire.
Ainsi, nous pouvons chercher comme ça en utilisant Hibernate:
Post post = entityManager.unwrap(Session.class)
.bySimpleNaturalId(Post.class)
.load(slug);
Et Hibernate exécutera les deux requêtes suivantes:
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 première requête est nécessaire pour résoudre l'identifiant de l'entité associée à l'identifiant naturel fourni.
La deuxième requête est facultative si l'entité est déjà chargée dans le cache de premier ou de second niveau.
Comme je l'ai expliqué dans this article, la raison pour avoir la première requête est parce que Hibernate a déjà une logique bien établie pour charger et associer des entités par leur identifiant dans le contexte de persistance.
Maintenant, si vous voulez ignorer la requête d'identification de l'entité, vous pouvez facilement annoter l'entité en utilisant l'annotation @NaturalIdCache
:
@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);
}
}
De cette façon, vous pouvez chercher l'entité Post
sans même frapper la base de données. Cool, n'est-ce pas?
Le hachage (et il n'a pas besoin d'être un hachage car une clé peut être multi-colonne) ne serait une clé naturelle valide que si les données ne peuvent pas changer (e-mail est bon, le nom est iffy, mot de passe est improbable et l'âge est faux). – Tordek
@Chris S: Pas de vis-à-vis: "substitut" – gbn
@Tordek: bon point. @Gbn Mis à jour le texte un peu. Les articles wikipedia ont de bonnes explications des deux –