This guide discusses migration to Hibernate ORM version 6.6. For migration from earlier versions, see any other pertinent migration guides as well.
Oracle implicit array types
The names for implicitly created array types on Oracle have slightly changed to account for converted types. Previously, the naming of implicit array types was only using the Java type simple name which could conflict when the same Java type is used with different JDBC type codes or converters. To avoid name clashes, the naming of implicitly created array types now also includes the preferred Java type simple name of the JDBC type in case the preferred Java type differs from the field type. In case of converted types, the converter Java class simple name is used instead.
The array type for a persistent property of type BigInteger[]
was previously BigIntegerArray
and would now be BigIntegerBigDecimalArray
, because the preferred Java type for the NUMERIC
/DECIMAL
JDBC type is BigDecimal
.
To specify a custom array type name, annotate the persistent property with @Column(columnDefinition = "BigIntegerArray")
.
Changes to UserDefinedType
UserDefinedType
was renamed to UserDefinedObjectType
and everything except access to column information
was abstracted in a new interface named UserDefinedType
. This was done to allow modelling dependencies between
named arrays, modeled as UserDefinedArrayType
extending the new UserDefinedType
interface,
and UserDefinedObjectType
i.e. arrays of structs.
UserDefinedType
was not explicitly annotated with @Incubating
before,
but it was introduced for the incubating @Struct
feature in ORM 6.2,
which made it effectively incubating as well. To make this more clear,
the types were now also explicitly marked as @Incubating
.
The changes affect users which previously queried or created UserDefinedType
in a Namespace
.
Methods that return or operate on UserDefinedType
have been marked as @Incubating
to make it clear that these contracts might still evolve.
Another change is to the already incubating ColumnOrderingStrategy
,
where the argument type of orderUserDefinedTypeColumns
was changed from UserDefinedType
to UserDefinedObjectType
.
Subset check for arrays to use array_includes
Support for array_contains()
to accept an array as element argument is deprecated and will emit a warning.
To check if an array is a subset of another array, use the array_includes()
function,
or the new INCLUDES
predicate i.e. array INCLUDES subarray
.
Merge versioned entity when row is deleted
Previously, merging a detached entity resulted in a SQL insert
whenever there was no matching row in the database (for example, if the object had been deleted in another transaction).
This behavior was unexpected and violated the rules of optimistic locking.
An OptimisticLockException
is now thrown when it is possible to determine that an entity is definitely detached, but there is no matching row.
For this determination to be possible, the entity must have either:
-
a generated
@Id
field, or -
a non-primitive
@Version
field.
For entities which have neither, it’s impossible to distinguish a new instance from a deleted detached instance, and there is no change from the previous behavior.
Explicit validation of annotated class types
Hibernate has always been lax when it comes to @Embedded
property types, allowing classes not annotated with @Embeddable
to still work correctly. This is a nice feature that enables you to map your attributes to classes that you cannot modify to add the annotation, and will continue to work as expected in the future.
One consequence of this, though, was letting you use both @MappedSuperlcass
and @Embeddable
on the same annotated class as a workaround to enable having subclasses annotated as @Embeddable
and still use both types in embedded attribute mappings. This has never been officially supported, with things like the JPA static metamodel not working as expected, and starting from 6.6 we will explicitly validate that mapped classes are annotated either @MappedSuperclass
or @Embeddable
, or @Entity
.
Extending a @MappedSuperclass
annotated class with an @Embeddable
type is still supported, but we suggest keeping the two annotated class types separate. You can now also take advantage of explicit discriminator column based embeddable inheritance.
Discriminator-based embeddable inheritance
ORM 6.6 introduced support for @Embeddable
type inheritance, always relying on a discriminator column stored within the entity mappings that contain the polymorphic @Embedded
property of that type.
Note that this functionality will be automatically enabled for all @Embedded
properties whose type (Java class) is extended by subclasses annotated with @Embeddable
. Previously, @Embeddable
-annotated subtypes were always ignored, so this should not impact your mappings, unless you were using the "workaround" described in the previous chapter.
With embeddable inheritance, we also enabled the type()
and treat()
functions to work with embeddable-typed paths.
As a consequence, the org.hibernate.query.sqm.tree.domain.SqmTreatedPath#getTreatTarget()
method will now return a generic ManagedDomainType
object,
which could in turn be an EntityDomainType
(as it was before) or also an EmbeddableDomainType
instance.
You can find more details about embeddable inheritance in the dedicated user guide chapter.
H2 database and bulk mutation strategy
With ORM 6.6 when a bulk mutation involves multiple tables, H2 dialect will make use of global temporary tables instead of local ones.
Criteria: jakarta.persistence.criteria.Expression#as(Class)
The behaviour of jakarta.persistence.criteria.Expression#as(Class)
has been changed to conform to the Jakarta Persistence specification.
Expression.as()
doesn’t do anymore a real type conversions, it’s just an unsafe typecast on the Expression object itself.
In order to perform an actual typecast, org.hibernate.query.criteria.JpaExpression#cast(Class)
can be used.
E.g.
( (JpaExpression) from.get( "theInt" ) ).cast( String.class )
@Table and SINGLE_TABLE inheritance
In previous versions, @Table
on a subclass in a SINGLE_TABLE hierarchy was simply ignored. E.g.
@Entity
@Inherited
class RootClass {
// ...
}
@Entity
@Table(...)
class SubClass extends RootClass {
// ...
}
As part of a larger effort toward better annotation validation, this now results in a mapping exception.
All classes in the hierarchy are stored to a single table as defined by the root. @Table
on the subclasses is,
at best, confusing.