252 lines
10 KiB
Plaintext
252 lines
10 KiB
Plaintext
= 7.0 Migration Guide
|
|
:toc:
|
|
:toclevels: 4
|
|
:docsBase: https://docs.jboss.org/hibernate/orm
|
|
:versionDocBase: {docsBase}/7.0
|
|
:userGuideBase: {versionDocBase}/userguide/html_single/Hibernate_User_Guide.html
|
|
:javadocsBase: {versionDocBase}/javadocs
|
|
|
|
|
|
This guide discusses migration to Hibernate ORM version 7.0. For migration from
|
|
earlier versions, see any other pertinent migration guides as well.
|
|
|
|
[[jpa-32]]
|
|
== Jakarta Persistence 3.2
|
|
|
|
7.0 migrates to Jakarta Persistence 3.2 which is fairly disruptive, mainly around:
|
|
|
|
* type parameters
|
|
** Affects much of the Criteria API - especially roots, joins, paths
|
|
** Affects much of the Graph API -
|
|
*** org.hibernate.graph.Graph.addAttributeNode(java.lang.String) defines a return while
|
|
`jakarta.persistence.Graph.addAttributeNode(java.lang.String)` does not.
|
|
* new JPA features colliding with previous Hibernate extension features
|
|
** `Nulls` (JPA) v. `NullPrecedence` (Hibernate), including JPA's new `Order#getNullPrecedence()` returning `Nulls`
|
|
colliding with Hibernate's `SqmSortSpecification#getNullPrecedence` returning `NullPrecedence`. Hibernate's form
|
|
was renamed to `SqmSortSpecification#getHibernateNullPrecedence` to avoid the collision.
|
|
** `SchemaManager` is now also a JPA contract exposed as `EntityManagerFactory#getSchemaManager` which leads to type issues for
|
|
Hibernate's `SessionFactory#getSchemaManager`. Hibernate's `SchemaManager` now extends the new JPA `SchemaManager`.
|
|
But that is a bytecode incompatibility.
|
|
** JPA has added support in its Graph API for things Hibernate has supported for some time. Some of those are collisions
|
|
requiring changes to the Hibernate API.
|
|
** `Transaction#getTimeout`. JPA 3.2 adds `#getTimeout` but uses `Integer` whereas Hibernate has historically used `int`. Note that this raises the possibility of a `NullPointerException` during migration if, e.g., performing direct comparisons on the timeout value against an in (auto unboxing).
|
|
|
|
See this https://in.relation.to/2024/04/01/jakarta-persistence-3/[blog post] for a good discussion of the changes in Jakarta Persistence 3.2.
|
|
|
|
|
|
[[hibernate-models]]
|
|
== Hibernate Models
|
|
|
|
For many years Hibernate has used the Hibernate Commons Annotations (HCANN) library for handling various low-level tasks
|
|
related to understanding the structure of an application domain model, reading annotations and weaving in XML
|
|
mapping documents.
|
|
|
|
However, HCANN suffers from a number of limitations that continue to be problematic. And given
|
|
the use of HCANN across multiple projects, doing the needed refactoring was simply not possible.
|
|
|
|
The https://github.com/hibernate/hibernate-models[Hibernate Models] project was developed to be a better alternative
|
|
to HCANN. Hibernate Models is essentially an abstraction over reflection (`Type`, `Class`, `Member`, ...) and
|
|
annotations. Check out its project page for complete details.
|
|
|
|
7.0 uses Hibernate Models in place of HCANN.
|
|
|
|
NOTE: Currently, the `hibernate-envers` module still uses HCANN. That will change during continued 7.0 development.
|
|
|
|
|
|
|
|
[[annotation-validation]]
|
|
== Annotation Validations
|
|
|
|
7.0 adds many more checks about illegal use of annotations.
|
|
|
|
|
|
=== PersistentAttributeType
|
|
|
|
As of 7.0, Hibernate applies much better validation of an attribute specifying multiple PersistentAttributeTypes.
|
|
Jakarta Persistence 3.2 has clarified this in the specification. E.g., the following examples are all now illegal -
|
|
|
|
[source,java]
|
|
----
|
|
@Basic
|
|
@ManyToOne
|
|
private Employee manager;
|
|
----
|
|
|
|
or
|
|
|
|
[source,java]
|
|
----
|
|
@Lob
|
|
@ManyToOne
|
|
private Employee manager;
|
|
----
|
|
|
|
|
|
[[misplaced-annotations]]
|
|
=== Misplaced Annotations
|
|
|
|
7.0 does much more in-depth checking that annotations appear in the proper place. While previous versions
|
|
did not necessarily throw errors, in most cases these annotations were simply ignored. E.g.
|
|
|
|
|
|
[source,java]
|
|
----
|
|
@Entity
|
|
class Book {
|
|
// defines FIELD access-type
|
|
@Id
|
|
Integer id;
|
|
|
|
// previously ignored, this is an error now
|
|
@Column(name="category")
|
|
String getType() { ... }
|
|
|
|
...
|
|
}
|
|
----
|
|
|
|
|
|
[[auto-cascade-persist]]
|
|
== Cascading persistence for `@Id` and `@MapsId` fields
|
|
|
|
Previously Hibernate automatically enabled `cascade=PERSIST` for association fields annotated `@Id` or `@MapsId`.
|
|
This was undocumented and unexpected behavior, and arguably against the intent of the Persistence specification.
|
|
|
|
Existing code which relies on this behavior should be modified by addition of explicit `cascade=PERSIST` to the association field.
|
|
|
|
|
|
[[enum-checks]]
|
|
== Enums and Check Constraints
|
|
|
|
Hibernate previously added support for generating check constraints for enums mapped using `@Enumerated`
|
|
as part of schema generation. 7.0 adds the same capability for enums mapped using an `AttributeConverter`,
|
|
by asking the converter to convert all the enum constants on start up.
|
|
|
|
|
|
[[java-beans]]
|
|
== JavaBean Conventions
|
|
|
|
Previous versions allowed some questionable (at best) attribute naming patterns. These are no longer supported. E.g.
|
|
|
|
[source,java]
|
|
----
|
|
@Basic
|
|
String isDefault();
|
|
----
|
|
|
|
|
|
|
|
[[cleanup]]
|
|
== Some Cleanup
|
|
|
|
* Removed `SqmQualifiedJoin`. All joins are qualified.
|
|
* Removed `AdditionalJaxbMappingProducer`, deprecated in favor of `AdditionalMappingContributor`
|
|
* Removed `MetadataContributor`, deprecated in favor of `AdditionalMappingContributor`
|
|
* Removed `@Persister`.
|
|
* Removed `hibernate.mapping.precedence` and friends
|
|
* Removed `org.hibernate.Session#save(Object object)` and `org.hibernate.Session#save(String entityName, Object object)` in favor of `org.hibernate.Session#persist(Object object)` and `org.hibernate.Session#persist(String entityName, Object object)`
|
|
* Removed `org.hibernate.Session#saveOrUpdate(Object object)` and `org.hibernate.Session#saveOrUpdate(String entityName, Object object)` in favor `persist` if the entity is transient or `merge` if the entity is detached.
|
|
* Removed `org.hibernate.Session#update(Object object` and `org.hibernate.Session#update(String entityName, Object object)` in favor of `org.hibernate.Session.merge(T object)` and `org.hibernate.Session.merge(String entityName, T object)`
|
|
* Removed `org.hibernate.annotations.CascadeType.SAVE_UPDATE` in favor of `org.hibernate.annotations.CascadeType.PERSIST` + `org.hibernate.annotations.CascadeType.MERGE`
|
|
* Removed `@SelectBeforeUpdate`
|
|
* Removed `org.hibernate.Session#delete(Object object)` and `org.hibernate.Session#delete(String entityName, Object object)` in favor of `org.hibernate.Session#remove(Object object)`
|
|
* Removed `org.hibernate.annotations.CascadeType.DELETE` in favor of `org.hibernate.annotations.CascadeType#REMOVE`
|
|
|
|
|
|
[WARNING]
|
|
===
|
|
The removal of `CascadeType.SAVE_UPDATE` slightly changes the persist and flush behaviour (not affecting application using `Entitymanager`) that now conforms with the Jakarta JPA specifications.
|
|
|
|
Persisting a transient entity with an associated detached entity where the association is annotated with cascade=all or cascade=persist throws an exception if the detached entity has not been re-associated with the the session using lock or merge.
|
|
|
|
The same happens when flushing a managed entity having an associated detached entity.
|
|
|
|
```
|
|
@Entit
|
|
class Parent {
|
|
...
|
|
|
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
|
@LazyCollection(value = LazyCollectionOption.EXTRA)
|
|
private Set<Child> children = new HashSet<>();
|
|
}
|
|
|
|
@Entity
|
|
class Child {
|
|
|
|
...
|
|
|
|
@Id
|
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
|
private Long id;
|
|
|
|
@ManyToOne
|
|
private Parent parent;
|
|
}
|
|
|
|
|
|
```
|
|
|
|
```
|
|
// Being Child c1 detached.
|
|
|
|
scope.inTransaction(
|
|
session -> {
|
|
Parent parent = session.get( Parent.class, parentId );
|
|
// add detached Child c
|
|
parent.addChild( c1 );
|
|
}
|
|
);
|
|
```
|
|
will throw an `jakarta.persistence.EntityExistsException`
|
|
|
|
in order to fix the issue we can call `session.lock(c1,LockMode.NONE)` before adding `c1` to the `parent` or instead using `p.addChild( session.merge(c1) )`;
|
|
|
|
|
|
[[ddl-implicit-datatype-timestamp]]
|
|
== Default precision for timestamp on some databases
|
|
|
|
The default precision for Oracle timestamps was changed to 9 i.e. nanosecond precision.
|
|
The default precision for SQL Server timestamps was changed to 7 i.e. 100 nanosecond precision.
|
|
|
|
|
|
[[sf-name]]
|
|
== SessionFactory Name (and JNDI)
|
|
|
|
Hibernate defines `SessionFactory#getName` (specified via `cfg.xml` or `hibernate.session_factory_name`) which is used to
|
|
help with (de)serializing a `SessionFactory`. It is also, unless `hibernate.session_factory_name_is_jndi` is set to `false`,
|
|
used in biding the `SessionFactory` into JNDI.
|
|
|
|
This `SessionFactory#getName` method pre-dates Jakarta Persistence (and JPA). It now implements `EntityManagerFactory#getName`
|
|
inherited from Jakarta Persistence, which states that this name should come from the persistence-unit name.
|
|
To align with Jakarta Persistence (the 3.2 TCK tests this), Hibernate now considers the persistence-unit name if no
|
|
`hibernate.session_factory_name` is specified.
|
|
|
|
However, because `hibernate.session_factory_name` is also a trigger to attempt to bind the SessionFactory into JNDI,
|
|
this change to consider persistence-unit name, means that each `SessionFactory` created through Jakarta Persistence now
|
|
have a name and Hibernate attempted to bind these to JNDI.
|
|
|
|
To work around this we have introduced a new `hibernate.session_factory_jndi_name` setting that can be used to explicitly
|
|
specify a name for JNDI binding. The new behavior is as follows (assuming `hibernate.session_factory_name_is_jndi` is not explicitly configured):
|
|
|
|
* If `hibernate.session_factory_jndi_name` is specified, the name is used to bind into JNDI
|
|
* If `hibernate.session_factory_name` is specified, the name is used to bind into JNDI
|
|
|
|
Hibernate can use the persistence-unit name for binding into JNDI as well, but `hibernate.session_factory_name_is_jndi`
|
|
must be explicitly set to true.
|
|
|
|
|
|
[[hbm-transform]]
|
|
== hbm.xml Transformation
|
|
|
|
Previous versions of Hibernate performed transformations of `hbm.xml` files (with `hibernate.transform_hbm_xml.enabled=true`)
|
|
one file at a time. This is now done across the entire set of `hbm.xml` files at once.
|
|
While most users will never see this change, it might impact integrations which tie-in to
|
|
XML processing.
|
|
|
|
|
|
[[todo]]
|
|
== Todos (dev)
|
|
|
|
* Look for `todo (jpa 3.2)` comments
|
|
* Look for `todo (7.0)` comments |