Migrating to Hibernate 6: What’s new and what got changed?
All you need to know when Migrating to Hibernate 6

Hello Guys 🫶
In this article, I will show you the most important steps to prepare your application for Hibernate 6.x migration and what you need to do when migrating your application. Also, we will discuss the new changes coming along with Hibernate 6 as well.
What is Hibernate?
Hibernate is a Java-based object-relational mapping (ORM) tool that provides a framework to map object-oriented domain models to relational databases and vice versa. Hibernate is concerned with data persistence as it applies to relational databases (via JDBC).
What’s new in Hibernate 6?
Hibernate 6.0 brought us many internal improvements. Let us explore these changes.
Hibernate 6 is Using Jakarta Persistence API 3.x (JPA 3)
Starting with Hibernate 6.0.0.Final, Hibernate has updated the dependency of the Jakarta Persistence API (JPA) specification has been updated to version 3. This means we need to use jakarta.persistence.* packages instead of old javax.persistence.* packages.
This means that all of your configuration files and import statements need to be updated, So this is a breaking change.
Upgrading to Java 11 is another migration that needs to be done, if it hasn’t already. We advise you to switch to Java 17, as that is the minimum JDK version required for Spring Boot 3.
But Spring Boot 3 comes in to picture with Hibernate 6
Because Spring Boot and Spring Data JPA work hand in hand and Spring Boot 3 by default uses Hibernate 6, migrating to Java 17 is inevitable.
Default Sequence Naming
The first thing you should check after migrating your persistence layer to Hibernate 6 is the generation of unique primary key values. If you use database sequences and each entity does not have a specified sequence, you must make some changes in the new Hibernate API.
Here, we have defined the generation strategy to Sequence without defining the sequence exactly, as no name is defined. In this case, Hibernate uses the default sequence named hibernate_sequence.
@Entity(name="Person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
}Hibernate has changed this approach. Now Hibernate uses a different sequence for every entity class if it is not defined explicitly. The entity name and the postfix _SEQ make up the name of that sequence, which is Person_SEQ.
This is a breaking change because the entity-specific sequences don’t exist in the database.
There are 2 options to solve this problem:
- Update your database schema to add the new sequences.
- Add a configuration parameter to tell Hibernate to use the old default sequences.
Out of these 2 approaches, the second approach is the quickest and easiest way to solve the problem, and you can still add the new sequences in a future release.
By setting the
hibernate.id.db_structure_naming_strategyattribute in yourpersistence.xmlfile, you can instruct Hibernate to use the previous default sequences. You can configureHibernate <5.3’sdefault sequences by setting this value tosingleand you can configure theHibernate >=5.3default sequence names by configuring the valuelegacy.
<persistence>
<persistence-unit name="my-persistence-unit">
...
<properties>
<!-- ensure backward compatibility -->
<property name="hibernate.id.db_structure_naming_strategy" value="legacy" />
...
</properties>
</persistence-unit>
</persistence>If You are doing Hibernate 6 migration with Spring Boot, this can be configured with the property;
#Hibernate >=5.3
spring.jpa.properties.hibernate.id.db_structure_naming_strategy: legacy
#Hibernate < 50.3
spring.jpa.properties.hibernate.id.db_structure_naming_strategy: singleChanges in Instant and Duration Mapping
Another breaking change you will notice when migrating to Hibernate 6 is the mapping of Instant and Duration.
In Hibernate 5, Instant is mapped to SqlType.TIMESTAMP and Duration is mapped to Types.BIGINT. This mapping was changed in Hibernate 6. It now maps Instant to SqlType.TIMESTAMP_UTC and Duration to SqlType.INTERVAL_SECOND.
Since this is a breaking change, it will break the table mapping of existing applications. If you run into that problem, by setting the hibernate.type.preferred_duration_jdbc_type attribute to BIGINT and hibernate.type.preferred_instant_jdbc_type to TIMESTAMP you can resolve the issue.
<persistence>
<persistence-unit name="my-persistence-unit">
<properties>
<!-- ensure backward compatibility -->
<property name="hibernate.type.preferred_duration_jdbc_type" value="BIGINT" />
<property name="hibernate.type.preferred_instant_jdbc_type" value="TIMESTAMP" />
...
</properties>
</persistence-unit>
</persistence>Hibernate 6 introduced these two additional configuration parameters. But both of them are marked as Incubating meaning they might get changed or removed in the future.
The UUID Generation approach was changed
The UUID generator was implicit in Hibernate version 5. Hibernate automatically detected the UUID random generator after detecting the kind of ID field. But starting with Hibernate 6, we need to define the UUID primary key in the following way:
@Id
@UuidGenerator(style = UuidGenerator.Style.AUTO)
@GeneratedValue
private UUID id;This way, we will define the generator and UUID generation style explicitly. UuidGenerator.Style is a value out of the following types:
AUTO: Defaults to RANDOM
UUID.randomUUID() to generate values
Easier Mapping of JSON Documents
Hibernate 6 simplifies translating JSON documents to Java objects and storing them in your database. To instruct Hibernate on how to read and write a JSON document to a database, as well as how to serialize and deserialize it, you had to define a custom UserType in Hibernate versions 4 and 5.
Hibernate can map a JSON document to an Embeddable as of version 6. An embeddable object is a Java class whose state maps to multiple columns of a table, but which doesn’t have its own persistent identity. It’s a class with mapped attributes but no @Id attribute. This way, it becomes part of the entity definition and gets mapped to the same database table.
You define an embeddable by implementing a Java class and annotating it with @Embeddable.
@Embeddable
public class Address implements Serializable {
private String street;
private String postalCode;
}You can use the embeddable as an entity attribute once you’ve declared it. That required adding additional @Embedded annotation to the property. It instructs Hibernate to get additional mapping data from the embeddable class.
If you want to map the embeddable to a JSON document, you also need to annotate it with @JdbcTypeCode(SqlTypes.JSON) and include a JSON mapping library in your classpath.
@Entity(name="Person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@Embedded
@JdbcTypeCode(SqlTypes.JSON)
private Address address;
}After that, Hibernate stores the attribute in the database by serializing and deserializing it to a JSON document. Your database dialect determines the type of column to which Hibernate maps the attribute.
Support for Records as Embeddables
Hibernate only supported records as DTO projections until Hibernate 6. But with the release of Hibernate 6, you can map Records as embeddables. But unfortunately, you can’t use records to model your entities.
For every record you wish to use as an embeddable in versions 6.0 and 6.1, you must implement an EmbeddableInstantiator. The EmbeddableInstantiator is a proprietary Hibernate interface that tells Hibernate how to instantiate an embeddable object. This allows you to model an embeddable as a record and gets rid of JPA’s requirement for a no-argument constructor.
Hibernate offers an EmbeddableInstantiator of its own for all embeddable records, starting with Hibernate version 6.2.
You define an embeddable record by annotating your record with an Embeddable annotation. You need to annotate it with EmbeddableInstantiator and reference your instantiator implementation if your hibernate version is 6.0 or 6.1.
@Embeddable
@EmbeddableInstantiator(PersonInitiator.class)
public record Person (String id, String name) {}public class PersonInitiator implements EmbeddableInstantiator {
public Object instantiate(ValueAccess valuesAccess, SessionFactoryImplementor sessionFactory) {
final String id = valuesAccess.getValue(0, String.class);
final String name = valuesAccess.getValue(1, String.class);
return new Person( id, name );
}
}Thank You for Reading
- Please feel free to share your feedback.
- Stay connected for more insightful content.






