Hibernate Natural Ids with @NaturalId

Hibernate 4 had brought lots of improvements and @NaturalId is one of such nice improvements. Learn to create and work with natural ids in this tutorial.

1. What is a Natural Id?

Hibernate entities have @Id annotation that marks a field primary id for that entity. But sometimes, we may have a field in the entity that can uniquely identify the entity, but for some reason, it is not a suitable primary key.

For example, a UserEntity can have id field of long type to uniquely identify the user in that system. At the same time, users may have an email that can also uniquely identify the user in the system. But we cannot use the email as the primary key because it will be used as a foreign key in many other places and also in link/join tables. Using email in these places would not make sense at all.

So we can say that a natural id can be a logical unique key in some of the cases, but they are not suitable to be used as a primary key in the system.

2. Using a Natural Id

2.1. @NaturalId Annotation

To create a natural id in hibernate, use @NaturalId annotation. We can apply the annotation on multiple attributes in the same entity, and even on composite attributes.

public class EmployeeEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer employeeId;

  @NaturalId
  @Column
  private String email;

  @Column
  private String firstName;

  @Column
  private String lastName;

  //Getters and setters are hidden for brevity
}

2.2. Session.byNaturalId()

Hibernate provides Session.byNaturalId() API for loading an entity by its natural id much as it offers for loading by its identifier (PK).

session.byNaturalId(EmployeeEntity.class)
     .using("email", "howtodoinjava@gmail.com")
     .load();

The above statement will generate the SQL WHERE clause with a condition on email field.

select
     e1_0.ID,
     e1_0.email,
     e1_0.firstName,
     e1_0.lastName 
 from
     Employee e1_0 
 where
     e1_0.email=?

In the case of multiple natural ids, we can use the using() method multiple times.

session.byNaturalId(EmployeeEntity.class)
     .using("email", "howtodoinjava@gmail.com")
     .using("someAttr", "attrValue")
     .using("anotherAttr", "attrValue")
     .load();

Note that if the entity does not define a natural id, trying to load an entity by its natural id will throw an exception.

As evident from the above usages, @NaturalId can be a good replacement for named queries where rather than creating new queries for these unique attributes, we can directly use query them using natural id API.

2.3. Session.bySimpleNaturalId()

The “simple” natural ids are defined based on just one attribute we can directly pass the corresponding value of that natural id attribute directly. We do not have multiple natural ids per entity in this case.

For example, the email field is the only natiraul id in the EmployeeEntity class so it qualifies for the simple natural id.

To query for simple natural ids, we can use bySimpleNaturalId() method as follows:

session.bySimpleNaturalId(EmployeeEntity.class)
        .load("howtodoinjava@gmail.com");

If we try to load() entity using bySimpleNaturalId() and there are more than one natural ids present in the entity than the method will throw HibernateException.

HibernateException: Cannot interpret natural-id value [howtodoinjava@gmail.com] for compound natural-id

3. Proxy vs Initialized Entity

The NaturalIdLoadAccess class has three methods to get entity from database and they all differ in what would be loaded at the final result. Lets take a loom at all three methods.

  • load(): returns the persistent instance with the natural id value(s) as passed in using() methods. Or it returns null if there is no such persistent instance. If the instance is already associated with the session, return that instance, initializing it if needed. This method never returns an uninitialized instance.
  • loadOptional(): same as load() but an Optional is returned to handle nullability.
  • getReference(): might return a proxied instance. Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.

4. Conclusion

In this hibernate tutorial, we learned the difference between a natural id and primary id. We learned to use @NaturalId annotation on entity classes.

We learned to create simple natural ids and multiple natural ids in the same entity. And then we learned to load the entities using special APIs for natural ids.

Happy Learning !!

Sourcecode on Github

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.