-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Migration Guide 3.0: Hibernate ORM 5 to 6 migration
- What is this?
- API
- Configuration
-
Database schema and data serialization/deserialization changes
- Best-effort Hibernate ORM 5.6 compatibility switch
- One identifier generator (sequence/table) per entity hierarchy instead of a single
hibernate_sequence
- Increment size for sequences defaults to 50 instead of 1
enum
,Duration
,UUID
,Instant
,ZonedDateTime
,OffsetDateTime
,OffsetTime
properties may be persisted/loaded differently- Some databases and older database versions are no longer supported in Hibernate ORM
This guide is intended as a quick reference about the most common issues when migration from Hibernate ORM 5.6 to 6.2 in the context of Quarkus (Quarkus 2 to 3).
It is not intended as a complete reference, but should be updated regularly based on issues most reported by the community.
For a complete reference, refer to the official Hibernate ORM migration guides:
For changes related to Quarkus specifically, and not to the Hibernate ORM 5.6 → 6.2 migration, see the main migration guide.
- Affected applications
-
Any application using JPA directly. The vast majority of Hibernate ORM applications use JPA directly to some extent.
Such application would import JPA packages (
import javax.persistence.<something>;
) or use JPA query hints (query.setHint("javax.persistence.<something>", …)
) - Breaking changes
-
Hibernate ORM 6 implements Jakarta Persistence instead of Java EE Persistence API.
See here for more information.
- Symptoms
-
-
Compilation failure.
-
Ignored query hints.
-
- Actions
-
-
Replace all references to packages
javax.persistence.
withjakarta.persistence.
-
Replace all references to query hints starting with
javax.persistence.
withjakarta.persistence.
. Alternatively (and preferably), use constants fromorg.hibernate.jpa.AvailableHints
instead of hardcoding the hint names.
-
- Affected applications
-
Any application importing legacy Hibernate Criteria API types.
Such applications would necessarily call
Session#createCriteria
at some point. - Breaking changes
-
The deprecated, legacy Hibernate Criteria API (
org.hibernate.Criteria
) has been removed.See here for more information.
- Symptoms
-
Compilation failure.
- Actions
-
Migrate existing code that relies on legacy Hibernate Criteria API to the JPA Criteria API.
- Affected applications
-
Any application relying on custom types.
Such application would include classes that implement
BasicType
,UserType
,CompositeUserType
,JavaTypeDescriptor
,SqlTypeDescriptor
or similar, or register such classes through custom dialects or@TypeDef
, or refer to such classes with@Type
/@CollectionId#type
/@AnyMetaDef#metaType
/@AnyMetaDef#idType
. - Breaking changes
-
Custom type interfaces and abstract classes changed, requiring adjustments to implementations.
Type reference annotations changed (some were removed, some had their attributes change), requiring adjustments to explicit type uses.
See here for more information.
- Symptoms
-
Compilation failure in custom type implementations and/or type references through
@TypeDef
/@Type
/etc. - Actions
-
-
Double-check that you still need custom types. Hibernate ORM 6 provides more built-in types than ever:
-
Collections/arrays of basic values are stored as native database arrays where supported, by default.
-
Enums are stored as native database enums where supported, by default.
-
ZonedDateTime
,OffsetDateTime
andOffsetTime
are stored as atimestamp with time zone
where supported, by default. -
Instant
is stored as atimestamp with time zone
where supported, by default. -
UUIDs are stored using native database types where supported (e.g. MariaDB or Microsoft SQL Server), by default.
-
Binary/text types are mapped to the most appropriate native database type depending on their length, so for example
@Column(length = org.hibernate.Length.LONG32)
will be mapped totext
on PostgreSQL, by default. -
You can select database types explicitly by annotating properties e.g. with
@JdbcTypeCode(SqlTypes.LONG32VARCHAR)
(which would map totext
on PostgreSQL). -
And more. In general, try removing your custom type and check (in a development environment) if Hibernate ORM generates what you need, and if not you can also try to force the database type with
@JdbcTypeCode(SqlTypes.<what you want>)
.
-
-
If you still need a custom type, refer to this guide to find out the appropriate solution in Hibernate ORM 6.
-
- Affected applications
-
Any application using JPQL/HQL/SQL strings for queries, or equivalent
Criteria
queries, and relying on the syntax elements listed below. - Breaking changes
-
Hibernate ORM 6 made a few changes to the HQL syntax:
-
The optional
from
keyword (update from MyEntity e set e.attr = null
) is now disallowed forupdate
queries. -
Column names are no longer accepted in HQL queries: Hibernate ORM will only understand JPA attribute names (~Java field name), treating column names as unresolvable attributes.
-
Comparing entities directly to IDs is no longer allowed, be it root entities (
where myentity = :param
) or associations (where myentity.association = :param
). -
count()
in native queries will now return aLong
instead of aBigInteger
. -
Collection pseudo-attributes such as
.size
,.elements
,.indices
,.index
,.maxindex
,.minindex
,.maxelement
,.minelement
are not longer supported.
-
- Symptoms
-
Runtime exceptions, either while parsing the query (for no-longer-supported syntax elements) or when converting the result type (for
count()
). - Actions
-
-
Remove the
from
keyword from yourupdate
queries:update from MyEntity e set e.attr = null
⇒update MyEntity e set e.attr = null
. -
If entities have attributes mapped to columns with a different name (e.g. attribute
myText
mapped to columnmy_text
), make sure to use the attribute name in HQL queries and never the column name. -
Reference identifiers explicitly instead of referencing entities where identifier comparisons are involved:
where myentity = :param
⇒where myentity.id = :param
,where myentity.association = :param
⇒where myentity.association.id = :param
. -
Adjust your native queries returning
count()
to expect a result of typeLong
instead ofBigInteger
. -
Replace collection pseudo-attributes with the equivalent functions:
-
mycollection.size
⇒size(mycollection)
-
mycollection.elements
⇒value(mycollection)
-
mycollection.indices
⇒index(mycollection)
(for lists) orkey(mycollection)
(for maps) -
mycollection.maxindex
⇒maxindex(mycollection)
-
mycollection.minindex
⇒minindex(mycollection)
-
mycollection.maxelement
⇒maxelement(mycollection)
-
mycollection.minelement
⇒minelement(mycollection)
-
-
- Affected applications
-
Any application configuring a Hibernate ORM dialect explicitly.
Such application would use the configuration property
quarkus.hibernate-orm.dialect
inapplication.properties
/application.yaml
, or the configuration propertyhibernate.dialect
inpersistence.xml
. - Breaking changes
-
Hibernate ORM 6 is now able to handle multiple versions and spatial variants of a database within a single dialect class; as a result, version-specific and spatial-specific dialect classes such as
org.hibernate.dialect.PostgreSQL91Dialect
ororg.hibernate.spatial.dialect.postgis.PostgisPG94Dialect
are deprecated and will lead to warnings on startup.See here for more information.
Additionally, some dialects have moved to a new Maven artifact,
org.hibernate.orm:hibernate-community-dialects
, because they are no longer supported in Hibernate ORM Core.See here for more information about dialects that are supported in Hibernate ORM Core, and here for more information about supported versions.
Finally, the Quarkus-specific H2 dialect
io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect
was removed as it is no longer necessary. - Symptoms
-
-
Deprecation warning about the dialect on startup.
-
Build or startup failure if the application attempts to use dialects that have been removed such as
io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect
or that moved tohibernate-community-dialects
.
-
- Actions
-
-
Change the configuration property
quarkus.hibernate-orm.dialect
(inapplication.properties
) /hibernate.dialect
(inpersistence.xml
):-
If you use
application.properties
, and your database is officially supported in Quarkus, and you used the dialect for a database version that is still supported in Hibernate ORM Core:-
Remove the configuration property: it is no longer necessary and will be inferred automatically.
-
-
Otherwise, if you used a dialect for a database version that is still supported in Hibernate ORM Core:
-
Switch to the corresponding version-independent dialect class, e.g.
org.hibernate.dialect.CockroachDialect
instead oforg.hibernate.dialect.CockroachDB201Dialect
.
-
-
Otherwise (i.e. you used a dialect for a database or database version that is no longer supported in Hibernate ORM Core,):
-
Add a Maven/Gradle dependency to
org.hibernate.orm:hibernate-community-dialects
. -
Switch to the relevant version-independent dialect class, e.g.
org.hibernate.community.dialect.OracleLegacyDialect
instead oforg.hibernate.dialect.Oracle9iDialect
ororg.hibernate.community.dialect.MariaDBLegacyDialect
instead oforg.hibernate.dialect.MariaDB102Dialect
. -
Seriously consider upgrading your database to the latest version, because your application might stop working at any moment with the next versions of Quarkus.
-
-
-
Set the database version through configuration property
quarkus.datasource.db-version
(inapplication.properties
) /jakarta.persistence.database-product-version
(inpersistence.xml
).
-
If you are in a hurry and want to address API/behavior issues and keep (most) schema changes for later, you can take advantage of the Hibernate ORM 5.6 database compatibility switch:
quarkus.hibernate-orm.database.orm-compatibility.version = 5.6
When this property is set, Quarkus attempts to configure Hibernate ORM to exchange data with the database as the given version of Hibernate ORM would have, on a best-effort basis.
Warning
|
|
One identifier generator (sequence/table) per entity hierarchy instead of a single hibernate_sequence
Note
|
This is addressed by the 5.6 compatibility switch. |
- Affected applications
-
-
Any application defining an entity that extends
PanacheEntity
-
Any application defining an entity that uses
@GeneratedValue
on an entity identifier without assigning an identifier generator explicitly (no@SequenceGenerator
/@TableGenerator
/@GenericGenerator
).
-
- Breaking changes
-
Hibernate ORM will now assign a distinct identifier generator to each entity hierarchy by default.
This means Hibernate ORM will expect a different database schema, where each entity hierarchy has its own sequence, for example, instead of a single
hibernate_sequence
for all entities.See here for more information.
- Symptoms
-
-
If schema validation is enabled, it will fail, mentioning missing or undefined sequences/tables, e.g.
ERROR: relation "hibernate_sequence" does not exist
. -
Otherwise, SQL import scripts or persisting new entities will fail with exceptions referring to missing or undefined sequences/tables.
-
- Actions
-
-
Start your application with the setting
quarkus.hibernate-orm.database.generation=validate
to have Hibernate ORM log all schema incompatibilities, which will list all sequences that are missing. Depending on your mapping (e.g. if your mapping defines an identifier generator explicitly for each entity), there may not be any. -
Optionally, if the sequence/table names used by Hibernate ORM by default are not to your liking, configure the identifier generation of each entity explicitly: sequence, identity column or table. When using Panache, such (advanced) configuration requires extending
PanacheEntityBase
instead ofPanacheEntity
. -
Update your database schema:
-
Use DDL (SQL such as
create sequence […]
) to create the missing sequences/tables. -
Use DDL to initialize sequences/tables with the right value (essentially
max(id) + 1
).
-
-
Update any initialization script (e.g.
import.sql
) to work with the per-entity sequences instead ofhibernate_sequence
.
-
Note
|
This is addressed by the 5.6 compatibility switch. |
- Affected applications
-
-
Any application defining an entity that extends
PanacheEntity
-
Any application defining an entity that uses
@GeneratedValue
on an entity identifier without assigning an identifier generator explicitly (no@SequenceGenerator
/@TableGenerator
/@GenericGenerator
). -
Any application defining an entity that uses
@GeneratedValue
on an entity identifier with the corresponding identifier generator defined with@GenericGenerator
, using database sequences and an explicitly defined increment size.
-
- Breaking changes
-
Hibernate ORM will now default to 50 instead of 1 for the increment size of sequences.
Upon retrieving the next value of a sequence, Hibernate ORM will pool the retrieved value as well as the 49 next ones to use them as identifier IDs (Quarkus defaults to the
pooled-lo
optimizer).See here for more information.
- Symptoms
-
-
If schema validation is enabled, it will fail, mentioning an incorrect increment size for your sequences (actual 1, expected 50).
-
Otherwise, SQL import scripts or persisting new entities will fail with exceptions referring to duplicate keys or violated primary key constraints.
-
- Actions
-
-
Update your database schema:
-
Use DDL (SQL such as
alter sequence […]
) to set the increment size of your sequences to the right value, i.e. to the allocation size of your identifier generators, which defaults to 50 since Hibernate ORM 6.
-
-
If your use data import scripts and your application/tests assume that certain entities have a certain identifier, do not simply use
nextval('mysequence')
as its may no longer return the value you expect. Just hardcode those entity identifiers in your data import scripts and reset the relevant sequences at the end of the script.E.g. this:
INSERT INTO hero(id, name, otherName, picture, powers, level) VALUES (nextval('hibernate_sequence'), 'Chewbacca', '', 'https://www.superherodb.com/pictures2/portraits/10/050/10466.jpg', 'Agility, Longevity, Marksmanship, Natural Weapons, Stealth, Super Strength, Weapons Master', 5);
Should become this:
-- Hardcode the ID of the first Hero to the value "1", since the tests expect that value. INSERT INTO hero(id, name, otherName, picture, powers, level) VALUES (1, 'Chewbacca', '', 'https://www.superherodb.com/pictures2/portraits/10/050/10466.jpg', 'Agility, Longevity, Marksmanship, Natural Weapons, Stealth, Super Strength, Weapons Master', 5); -- Set the current value of the sequence so that the IDs of newly -- created entities won't conflict with the hardcoded identifiers above. ALTER SEQUENCE hero_seq RESTART WITH (select max(id) + 1 from hero);
Alternatively, if you do not wish to benefit from pooled identifier generation, set increment sizes explicitly in your mapping when you define identifier generators, using for example
@SequenceGenerator(…, allocationSize = 1)
instead of@GenericGenerator
. See here for more information. -
enum
, Duration
, UUID
, Instant
, ZonedDateTime
, OffsetDateTime
, OffsetTime
properties may be persisted/loaded differently
Note
|
This is addressed by the 5.6 compatibility switch, except for enum properties whose schema validation will fail but which should work correctly at runtime. |
- Affected applications
-
Any application with an entity property of the following types, unless it overrides the SQL type with
@Type
/@JdbcType
:-
any enum type, unless the property is annotated with
@Enumerated(STRING)
-
Duration
-
UUID
-
Instant
-
ZonedDateTime
-
OffsetDateTime
-
OffsetTime
-
- Breaking changes
-
Hibernate ORM will now map those Java types to different JDBC types, which depending on your database may map to a different SQL type:
-
enum types now map to
SqlTypes.TINYINT
,SqlTypes.SMALLINT
or a native enum type depending on the number of values and database support for enums, instead ofTypes.TINYINT
in Hibernate ORM 5. -
Duration
maps toSqlTypes.INTERVAL_SECOND
, instead ofTypes.BIGINT
in Hibernate ORM 5. -
UUID
maps toSqlTypes.UUID
, instead ofTypes.BINARY
in Hibernate ORM 5. -
Instant
maps toSqlTypes.TIMESTAMP_UTC
, instead ofTypes.TIMESTAMP
in Hibernate ORM 5. -
ZonedDateTime
andOffsetDateTime
map toSqlTypes.TIMESTAMP_WITH_TIMEZONE
instead ofTypes.TIMESTAMP
in Hibernate ORM 5. Additionally, if the database does not properly store timezones, values are normalized to UTC upon persisting, instead of the Hibernate ORM 5 behavior that was to normalize to the timezone configured throughquarkus.hibernate-orm.jdbc.timezone
, defaulting to the JVM timezone. -
OffsetTime
maps toSqlTypes.TIME_WITH_TIMEZONE
instead ofTypes.TIME
in Hibernate ORM 5. Additionally, if the database does not properly store timezones, values are normalized to UTC upon persisting, instead of the Hibernate ORM 5 behavior that was to normalize to the timezone configured throughquarkus.hibernate-orm.jdbc.timezone
, defaulting to the JVM timezone.For more information, see here for
Duration
, here for `UUID (and more specifically here forUUID
on MariaDB and here forUUID
on SQL Server), here forInstant
, here for enums (more context here), here forZonedDateTime
andOffsetDateTime
, here forOffsetTime
.
-
- Symptoms
-
-
If schema validation is enabled, it may fail, mentioning an incorrect type for your columns.
-
Otherwise:
-
For enum types, if your database supports native enum types (e.g. MySQL), persisting and loading entities will fail; otherwise they should work despite the discrepancy between your database schema and the one expected by Hibernate ORM.
-
For
ZonedDateTime
,OffsetDateTime
andOffsetTime
, if you were previously effectively using a normalization timezone (quarkus.hibernate-orm.jdbc.timezone
or JVM timezone) different from UTC, values loaded from the database may be incorrect. -
For all other types, persisting and loading should work correctly despite the discrepancy between your database schema and the one expected by Hibernate ORM.
-
-
- Actions
-
-
Start your application with the setting
quarkus.hibernate-orm.database.generation=validate
to have Hibernate ORM log all schema incompatibilities, which will list all columns with an incorrect type. -
Update your database schema:
-
Use DDL (SQL such as
alter table […] alter column […]
) to set the type of your columns to the right value. Depending on your database this might require multiple steps, e.g. renaming the old column, creating the new column, copying the data from the old to the new column, and finally deleting the old column.
-
-
For
ZonedDateTime
,OffsetDateTime
andOffsetTime
, if you were previously effectively using a normalization timezone (quarkus.hibernate-orm.jdbc.timezone
or JVM timezone) different from UTC:-
Use SQL
UPDATE
statements to convert data to the timezone expected by Hibernate ORM (UTC). -
Alternatively, configure timezone storage explicitly through configuration property
quarkus.hibernate-orm.mapping.timezone.default-storage
; setting it tonormalize
will give you Hibernate ORM 5’s behavior.
-
-
Warning
|
This is not addressed by the 5.6 compatibility switch. |
- Affected applications
-
Any application configuring a Hibernate ORM dialect explicitly, and using a dialect that targets either a unsupported database or an unsupported version of a supported database (see below for databases and versions supported in Hibernate ORM).
Such application would use the configuration property
quarkus.hibernate-orm.dialect
inapplication.properties
/application.yaml
, or the configuration propertyhibernate.dialect
inpersistence.xml
. - Breaking changes
-
Hibernate ORM 6 dropped official support for some databases, either fully or only for older versions.
See here for more information about dialects that are supported in Hibernate ORM Core, and here for more information about supported versions.
NoteQuarkus itself has stricter database and version restrictions. In Quarkus 3.0, we expect the following databases with the following minimum version to work out-of-the-box:
-
DB2 10.5 and later.
-
Apache Derby 10.14 and later.
-
MariaDB 10.6 and later.
-
Microsoft SQL Server 2016 and later.
-
MySQL 8 and later.
-
Oracle 12 and later.
-
PostgreSQL 10.0 and later.
-
H2 with the exact version defined in the Quarkus BOM.
Other database and versions may or may not work, may require additional configuration, and may suffer from limitations such as not working in native mode.
Additionally, upgrades to newer database versions may involve schema changes, as Hibernate ORM dialects targeting newer database versions may have different default schema expectations:
-
MariaDB:
-
MariaDBDialect
uses sequences by default for identifier generation, which is compatible withMariaDB106Dialect
(the default in Quarkus 2) andMariaDB103Dialect
, but not compatible withMariaDB102Dialect
(which uses ID generation tables).
-
-
Other databases:
-
No information at the moment. Feel free to open an issue to suggest more information to add to this guide.
-
-
- Symptoms
-
-
Deprecation warning about the dialect on startup.
-
Build or startup failure if the application attempts to use dialects that moved to
hibernate-community-dialects
.
-
- Actions
-
-
Follow instructions to configure your dialect and database version for backwards-compatible behavior.
-
If you use an older version of a supported database, seriously consider upgrading because your application might stop working at any moment with the next versions of Quarkus. This may involve changes to your database schema:
-
MariaDB < 10.2 to 10.3 and newer:
-
Optionally, configure the identifier generation of each entity explicitly: sequence, identity column or table.
-
Start your application with the setting
quarkus.hibernate-orm.database.generation=validate
to have Hibernate ORM log all schema incompatibilities, which will list all sequences that are missing. Depending on your mapping (e.g. if your mapping defines an identifier generator explicitly for each entity), there may not be any. -
Update your database schema:
-
Use DDL (SQL such as
create sequence […]
) to create the missing sequences. -
Use DDL to initialize sequences with the right value (essentially
max(id) + 1
).
-
-
-
Other databases:
-
No information at the moment. Feel free to open an issue to suggest more information to add to this guide.
-
-
-