2009-12-15 3 views
10

En lisant la documentation d'Hibernate, je continue de voir des références au concept d'identificateur naturel .Qu'est-ce qu'un identifiant naturel dans Hibernate?

Est-ce que cela signifie simplement l'identité d'une entité en raison de la nature des données qu'elle contient?

E.g. Le nom d'un utilisateur + mot de passe + âge + quelquechose est utilisé comme un composé identitifier?

Répondre

11

Dans Hibernate, les clés natrual sont souvent utilisées pour les recherches. Dans la plupart des cas, vous aurez un identifiant de substitution généré automatiquement. Mais cet identifiant est plutôt inutile pour les recherches, car vous interrogerez toujours par des champs tels que le nom, le numéro de sécurité sociale ou tout autre élément du monde réel. Lorsque vous utilisez les fonctions de mise en cache d'Hibernate, cette différence est très importante: Si le cache est indexé par votre clé primaire (identificateur de substitution), il n'y aura aucun gain de performance lors des recherches. C'est pourquoi vous pouvez définir un ensemble de champs que vous allez interroger la base de données avec - l'identifiant naturel. Hibernate peut ensuite indexer les données par votre clé naturelle et améliorer les performances de recherche.

Voir cet excellent blog post pour une explication plus détaillée ou ce RedHat page pour un exemple de fichier de mappage Hibernate.

8

Ce qui identifie naturellement une entité. Par exemple, mon adresse e-mail.

Cependant, une longue chaîne de longueur variable n'est pas une clé idéale, vous voudrez peut-être définir une surrogate id

Alias ​​Natural key dans la conception relationnelle

2

Un numéro de sécurité sociale pourrait être un natural identity, ou vous 'ai dit un hachage de l'information de l'utilisateur. L'alternative est surrogate key, par exemple un Guid/UID.

+0

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

+0

@Chris S: Pas de vis-à-vis: "substitut" – gbn

+0

@Tordek: bon point. @Gbn Mis à jour le texte un peu. Les articles wikipedia ont de bonnes explications des deux –

5

Un identificateur naturel est quelque chose qui est utilisé dans le monde réel en tant qu'identifiant. Un exemple est un numéro de sécurité sociale, ou un numéro de passeport.

Il est généralement une mauvaise idée d'utiliser des identifiants naturels comme clés dans une couche de persistance parce que a) ils peuvent être modifiés en dehors de votre contrôle, et b) ils peuvent finir par ne pas être unique en raison d'une erreur ailleurs, puis votre modèle de données ne peut pas le gérer, ce qui fait exploser votre application.

+0

On espère que la clé est contrainte, par exemple la contrainte de clé primaire pour réduire ces risques – gbn

+1

Vous pouvez le contraindre dans votre modèle de données, mais vous ne pouvez pas contraindre la vie réelle - des erreurs se produisent, et votre modèle de données n'a pas besoin de casser quand ils le font. Si vous avez besoin de corriger le numéro de sécurité sociale de quelqu'un, par exemple s'il a été entré de manière incorrecte, il devrait s'agir d'un seul UPDATE. Si vous l'avez utilisé comme une clé dans tout votre système ... sérialisé, stocké dans des sauvegardes, et peut-être même envoyé à des systèmes externes, vous êtes complètement foutu. Il n'y a aucun moyen que vous puissiez mettre à jour le SSN de cette personne sans casser quelque chose. PS: ne stockez pas de SSN du tout sauf si vous devez le faire. –

+1

Certes, il faut encore des contraintes et il devrait y avoir une différence entre le modèle logique et l'implémentation. SSN n'est pas unique non plus ... http://www.computerworld.com/s/article/300161/Not_So_Unique – gbn

1

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?

Questions connexes