Implementing Postgres UUID primary keys with Spring Boot and Hibernate
An alternative to auto-increment sequential primary keys, is using a Universally Unique Identifier
. Spring Boot and Hibernate support them and you can use them using Spring Data’s autogenerated SQL, JPQL and native queries.
As a demonstration project, you can clone spring-boot-hibernate-postgres-uuids.
To run the examples, you’ll need docker
and docker-compose
installed. You can setup the project by running docker-compose up
in the docker
directory. Doing this will create a new database, and automatically install the uuid-ossp
extension.
Creating the Entity model
The project uses a User
model as an example. You can setup Hibernate to use a UUID
for its entity ID by using the annotations below.
@Entity
@Table(name = "users", schema = "spring_boot_uuids", uniqueConstraints = {
@UniqueConstraint(columnNames = "email", name = "UNIQUE_EMAIL")
})
public class User
{
@Id
@Column
@Type(type="pg-uuid")
@GenericGenerator(name = "uuid-gen", strategy = "uuid2")
@GeneratedValue(generator = "uuid-gen")
private UUID id;
[snip]
}
Creating the Spring Data Repository
You can create a CrudRepository
using a UUID
as the identifier.
@Repository
public interface UserRepository extends CrudRepository<User, UUID>
{
[snip]
}
Now you can use the CrudRepository
autogenerated retrieve functions.
@Test
public void savesUserWithId()
{
User user = new User();
user.setEmail(TEST_EMAIL_ADDRESS);
User persistedUser = userRepository.save(user);
assertThat(persistedUser.getId(), is(not(nullValue())));
}
If you create a projection, it will work with that too.
public interface UserDisplay
{
UUID getId();
String getEmail();
@Value("#{T(java.time.Period).between(target.dateOfBirth, T(java.time.LocalDate).now()).getYears()}")
Integer getAge();
}
You can use the projection in combination with a JPQL query,
@Query("SELECT u.id AS id, " +
"u.email AS email, " +
"u.dateOfBirth AS dateOfBirth " +
"FROM User u WHERE u.id = :userId")
Optional<UserDisplay> getDisplayById(@Param("userId") UUID userId);
and a native query:
@Query(value = "SELECT CAST(u.id AS VARCHAR) AS id, " +
"u.email AS email," +
"u.date_of_birth AS dateOfBirth " +
"FROM spring_boot_uuids.users u " +
"WHERE u.id = :userId", nativeQuery = true)
Optional<UserDisplay> getNativeDisplayById(@Param("userId") UUID userId);
You’ll notice that the native query requires that the UUID
be cast to a VARCHAR
datatype. This is because currently Hibernate does not correctly recognise the raw UUID
type.