Most developers prefer numerical primary keys because they are efficient to use and easy to generate. But that doesn’t mean that a primary key has to be a number.
UUIDs, for example, have gained some popularity over the recent years. The main advantage of a UUID is its (practical) global uniqueness which provides a huge advantage for distributed systems.
If you use the typical, numerical ID that gets incremented for each new record, you need to generate all IDs by the same component of your system or the components need to communicate with each other. With a globally unique UUID, you don’t need all of this. Each component can generate a UUID and there will not be any conflicts.
Don’t want to read? You can watch it here!
On the other hand, the UUID also has some disadvantages. The most obvious one is its size. It’s 4 times larger than a numerical ID and can’t be handled as efficiently. You should, therefore, decide carefully if you want to use UUIDs or numeric IDs and discuss it with your database administrator.
If you decide to use UUIDs, you can, of course, also persist them with Hibernate. With just a few additional annotations, you can even let Hibernate generate the UUIDs for you.
Generate UUIDs with Hibernate
The IETF RFC 4122 defines 4 different strategies to generate UUIDs. Hibernate’s UUIDGenerator supports 2 of them:
- The default strategy generates the UUID based on random numbers (IETF RFC 4122 Version 4).
- You can also configure a generator that uses the IP address of the machine and a timestamp (IETF RFC 4122 Version 1).
Let’s have a look at the default strategy first.
Random number based UUID (IETF RFC 4122 version 4)
By default, Hibernate uses a random number based generation strategy. As always, you don’t have to do much to get the default behaviour. You just need to add a @GeneratedValue annotation that references the ID generator to your primary key attribute and define the generator with one of Hibernate’s @GenericGenerator annotations. The @GenericGenerator annotation requires 2 parameters, the name of the generator and the name of the class that implements the generator. In this case, it’s the org.hibernate.id.UUIDGenerator.
That’s all you have to do to let Hibernate generate UUIDs as primary key values. You can see an example of it in the following code snippet.
When you now persist a new Book entity, Hibernate generates a UUID before writing the new record to the database.
IP and timestamp based UUID (IETF RFC 4122 version 1)
Hibernate can also generate a UUID based on IETF RFC 4122 version 1. If you follow the specification, you should generate the UUID with the MAC address instead of the IP address. As long as nobody is messing around with it, the MAC address of each device should be unique and due to this help to create a unique UUID.
Hibernate uses the IP address instead of the MAC address. In general, this is not an issue. But if the servers of your distributed system are running in different networks you should make sure that none of them share the same IP address.
The configuration of the IETF RFC 4122 version 1 based UUID generator is similar to the previous one but requires an additional parameter that defines the generation strategy. You can see an example of it in the following code snippet. You just need to provide an additional @Parameter annotation with the name uuid_gen_strategy_class and the fully qualified classname of the generation strategy as the value.
When you now persist the new Book entity, Hibernate will use the CustomVersionOneStrategy class to generate the UUID based on IETF RFC 4122 version 1.
As you’ve seen, you can also use UUIDs as primary keys and let Hibernate handle the value generation. Hibernate’s UUIDGenerator supports the creation of version 1 and version 4 UUIDs as defined by IETF RFC 4122. By default, it generates version 4 UUIDs which is a good fit for most use cases.
Unfortunately, this is not part of the JPA specification and will require some adaptions, if you need to switch your JPA implementation.