O que é um identificador natural no Hibernate?

Ao ler a documentação do Hibernate, continuo vendo referências ao conceito de um identificador natural .

Isso significa apenas o id que uma entidade possui devido à natureza dos dados que detém?

Por exemplo, um nome de usuário + senha + idade + alguma coisa são usados ​​como um identificador composto?

No Hibernate, as chaves natruais são frequentemente usadas para pesquisas. Você terá um id substituto gerado automaticamente na maioria dos casos. Mas esse id é bastante inútil para pesquisas, como você sempre consultará por campos como nome, número de previdência social ou qualquer outra coisa do mundo real.

Ao usar os resources de cache do Hibernate, essa diferença é muito importante: se o cache for indexado por sua chave primária (id substituto), não haverá ganho de desempenho em pesquisas. É por isso que você pode definir um conjunto de campos com os quais você irá consultar o database – o id natural. O Hibernate pode então indexar os dados por sua chave natural e melhorar o desempenho da pesquisa.

Veja este excelente post para uma explicação mais detalhada ou esta página RedHat para um exemplo de arquivo de mapeamento do Hibernate.

O que identifica naturalmente uma entidade. Por exemplo, meu endereço de e-mail.

No entanto, uma cadeia longa de comprimento variável não é uma chave ideal, portanto, você pode definir um id substituto

AKA Chave natural em design relacional

Um identificador natural é algo que é usado no mundo real como um identificador. Um exemplo é um número de segurança social ou um número de passaporte.

Geralmente, é uma má idéia usar identificadores naturais como chaves em uma camada de persistência porque a) eles podem ser alterados fora de seu controle e b) eles podem acabar não sendo exclusivos devido a um erro em outro lugar e seu modelo de dados é possível manipulá-lo para que seu aplicativo exploda.

Em um sistema de database relacional, normalmente, você pode ter dois tipos de identificadores simples :

  • Chaves naturais, que são atribuídas por sistemas externos e garantidas como únicas
  • Chaves substitutas, como IDENTIDADE ou SEQUÊNCIA, atribuídas pelo database.

A razão pela qual as Surrogate Keys são tão populares é que elas são mais compactas (4 bytes ou 8 bytes), comparadas a uma Natural Key que é muito longa (por exemplo, o VIN leva 17 caracteres alfanuméricos, o ISBN do livro tem 13 dígitos).

Agora, se a Chave Substituta se tornar a Chave Primária, você a utilizará usando a anotação JPA @Id .

E, se você tem uma entidade que também tem uma chave natural, além do substituto, você pode mapeá-la com a anotação @NaturalId específica do Hibernate :

 @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); } } 

Agora, considerando a entidade acima, o usuário pode ter marcado um artigo do Post e agora deseja lê-lo. No entanto, a URL marcada contém o identificador natural slug , não a chave primária.

Então, podemos buscá-lo assim usando o Hibernate:

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

E o Hibernate irá executar as seguintes duas consultas:

 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 

A primeira consulta é necessária para resolver o identificador de entidade associado ao identificador natural fornecido.

A segunda consulta é opcional se a entidade já estiver carregada no primeiro ou no cache de segundo nível.

Como expliquei neste artigo , a razão para ter a primeira consulta é porque o Hibernate já possui uma lógica bem estabelecida para carregar e associar entidades pelo seu identificador no Contexto de Persistência.

Agora, se você quiser pular a consulta do identificador de entidade, poderá facilmente anotar a entidade usando a anotação @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); } } 

Dessa forma, você pode buscar a entidade Post sem bater no database. Legal certo?

Um número de previdência social pode ser uma identidade natural ou, como você disse, um hash das informações do usuário. A alternativa é uma chave substituta , por exemplo, um GUID / UID.

Identificador natural (também conhecido como chave comercial): é um identificador que significa ou representa algo na vida real.
E-mail ou identidade nacional para pessoa
Isbn for Book
IBAN para conta bancária

Essa anotação @NaturalId é usada para especificar o identificador natural.