Skip to content

Migration Guide 3.0: Hibernate ORM 5 to 6 migration

Guillaume Smet edited this page Mar 14, 2023 · 45 revisions

What is this?

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:

API

Packages and hints: javax.persistence. becomes jakarta.persistence.

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. with jakarta.persistence.

  • Replace all references to query hints starting with javax.persistence. with jakarta.persistence.. Alternatively (and preferably), use constants from org.hibernate.jpa.AvailableHints instead of hardcoding the hint names.

Legacy Hibernate Criteria API

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.

Configuration

Dialect configuration changes

Affected applications

Any application configuring a Hibernate ORM dialect explicitly.

Such application would use the configuration property quarkus.hibernate-orm.dialect in application.properties/application.yaml, or the configuration property hibernate.dialect in persistence.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 or org.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.

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 to hibernate-community-dialects.

Actions
  1. Change the configuration property quarkus.hibernate-orm.dialect (in application.properties) / hibernate.dialect (in persistence.xml):

  2. Set the database version through configuration property quarkus.datasource.db-version (in application.properties) / jakarta.persistence.database-product-version (in persistence.xml).

Database schema and data serialization/deserialization changes

Best-effort Hibernate ORM 5.6 compatibility switch

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
  • Schema validation may still fail in some cases: this attempts to make Hibernate ORM 6+ behave correctly at runtime, but it may still expect a different (but runtime-compatible) schema.

  • This does not address every breaking change. For example, older database versions that are no longer supported in Hibernate ORM Core will still not work correctly unless you take additional steps.

  • Robust test suites are still useful and recommended: you should still check that your application behaves as intended with your legacy schema.

  • This feature is inherently unstable: some aspects of it may stop working in future versions of Quarkus, and older versions will be dropped as Hibernate ORM changes pile up and support for those older versions becomes too unreliable.

  • You should still plan a migration of your schema to a newer version of Hibernate ORM. For help with migration, refer to the rest of this guide.

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 using @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.

  • Otherwise, SQL import scripts or persisting new entities will fail with exceptions referring to missing or undefined sequences/tables.

Actions
  1. Optionally, configure the identifier generation of each entity explicitly: sequence, identity column or table.

  2. 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.

  3. 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 (which might be more than the maximum identifier currently in use: see this section).

Increment size for sequences defined using @GenericGenerator defaults to 50 instead of 1

Note
This is addressed by the 5.6 compatibility switch.
Affected applications

Any application using @GeneratedValue on an entity identifier, with the corresponding identifier generator defined with @GenericGenerator and using database sequences, and without an explicitly defined increment size.

Breaking changes

Hibernate ORM will now assume a sequence mapped with @GenericGenerator has an increment size of 50 instead of 1 by default.

Upon retrieving the next value of a sequence, Hibernate ORM will pool the 50 previous integer values to use them as identifier IDs.

See here for more information.

Symptoms
  • If schema validation is enabled, it will fail, mentioning an incorrect increment size for your sequences.

  • Otherwise, SQL import scripts or persisting new entities will fail with exceptions referring to duplicate keys or violated primary key constraints.

Actions
  1. 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.

    • Use DDL to set the value of your sequences to something compatible with pooling, i.e. <the maximum identifier> + <increment/allocation size> + 1, e.g. (select max(id) + 50 + 1 from my_table).

  2. Update your SQL import scripts, if any:

    • When updating the value of sequences at the end of an SQL script, use the same formula as above.

  3. 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 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

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 of Types.TINYINT in Hibernate ORM 5.

  • Duration maps to SqlTypes.INTERVAL_SECOND, instead of Types.BIGINT in Hibernate ORM 5.

  • UUID maps to SqlTypes.UUID, instead of Types.BINARY in Hibernate ORM 5.

  • Instant maps to SqlTypes.TIMESTAMP_UTC, instead of Types.TIMESTAMP in Hibernate ORM 5.

  • ZonedDateTime and OffsetDateTime map to SqlTypes.TIMESTAMP_WITH_TIMEZONE instead of Types.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 through quarkus.hibernate-orm.jdbc.timezone, defaulting to the JVM timezone.

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, 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 and OffsetDateTime, 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
  1. 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.

  2. 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.

  3. For ZonedDateTime and OffsetDateTime, 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 to normalize will give you Hibernate ORM 5’s behavior.

Some databases and older database versions are no longer supported in Hibernate ORM

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 in application.properties/application.yaml, or the configuration property hibernate.dialect in persistence.xml.

Breaking changes

Hibernate ORM 6 dropped official support for some databases, either fully or only for older versions.

Note

Quarkus 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 with MariaDB106Dialect (the default in Quarkus 2) and MariaDB103Dialect, but not compatible with MariaDB102Dialect (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
  1. Follow instructions to configure your dialect and database version for backwards-compatible behavior.

  2. 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:

      1. Optionally, configure the identifier generation of each entity explicitly: sequence, identity column or table.

      2. 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.

      3. 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 (which might be more than the maximum identifier currently in use: see this section).

    • Other databases:

      • No information at the moment. Feel free to open an issue to suggest more information to add to this guide.

Current version

Migration Guide 3.17

Next version in main

Migration Guide 3.18

Clone this wiki locally