Skip to content

Commit

Permalink
HHH-15739 deprecate @LazyToOne and @LazyCollection
Browse files Browse the repository at this point in the history
and add some docs and cleanups
  • Loading branch information
gavinking committed Nov 25, 2022
1 parent c8ffee4 commit 5b5721f
Show file tree
Hide file tree
Showing 18 changed files with 913 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -990,19 +990,7 @@ See the <<chapters/domain/associations.adoc#associations-JoinFormula,`@JoinFormu
[[annotations-hibernate-lazycollection]]
==== `@LazyCollection`

The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/LazyCollection.html[`@LazyCollection`] annotation is used to specify the lazy fetching behavior of a given collection.
The possible values are given by the `https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/LazyCollectionOption.html[LazyCollectionOption]` enumeration:

`TRUE`:: Load it when the state is requested.
`FALSE`:: Eagerly load it.
`EXTRA`:: Prefer extra queries over full collection loading.

The `TRUE` and `FALSE` values are deprecated since you should be using the Jakarta Persistence {jpaJavadocUrlPrefix}FetchType.html[`FetchType`] attribute of the <<annotations-jpa-elementcollection>>, <<annotations-jpa-onetomany>>, or <<annotations-jpa-manytomany>> collection.

The `EXTRA` value has no equivalent in the Jakarta Persistence specification, and it's used to avoid loading the entire collection even when the collection is accessed for the first time.
Each element is fetched individually using a secondary query.

See the <<chapters/fetching/Fetching.adoc#fetching-LazyCollection, `@LazyCollection` mapping>> section for more info.
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Index.html[[line-through]#`@LazyCollection`#] annotation is deprecated.

[[annotations-hibernate-lazygroup]]
==== `@LazyGroup`
Expand All @@ -1019,15 +1007,7 @@ See the <<chapters/pc/BytecodeEnhancement.adoc#BytecodeEnhancement-lazy-loading-
[[annotations-hibernate-lazytoone]]
==== `@LazyToOne`

The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/LazyToOne.html[`@LazyToOne`] annotation is used to specify the laziness options, represented by https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/LazyToOneOption.html[`LazyToOneOption`], available for a `@OneToOne` or `@ManyToOne` association.

`LazyToOneOption` defines the following alternatives:

FALSE:: Eagerly load the association. This one is not needed since the Jakarta Persistence `FetchType.EAGER` offers the same behavior.
NO_PROXY:: This option will fetch the association lazily while returning real entity object.
PROXY:: This option will fetch the association lazily while returning a proxy instead.

See the <<chapters/domain/associations.adoc#associations-one-to-one-bidirectional-lazy,`@LazyToOne` mapping example>> section for more info.
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Index.html[[line-through]#`@LazyToOne`#] annotation is deprecated.

[[annotations-hibernate-listindexbase]]
==== `@ListIndexBase`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ The only way to figure out whether there is an associated record on the child si
Because this can lead to N+1 query issues, it's much more efficient to use unidirectional `@OneToOne` associations with the `@MapsId` annotation in place.

However, if you really need to use a bidirectional association and want to make sure that this is always going to be fetched lazily,
then you need to enable lazy state initialization bytecode enhancement and use the
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/LazyToOne.html[`@LazyToOne`] annotation as well.
then you need to enable lazy state initialization bytecode enhancement.

[[associations-one-to-one-bidirectional-lazy-example]]
.Bidirectional `@OneToOne` lazy parent-side association
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;

import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;

import org.junit.Test;

import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;

/**
* @author Vlad Mihalcea
*/
Expand Down Expand Up @@ -58,7 +54,6 @@ public static class Phone {
orphanRemoval = true,
fetch = FetchType.LAZY
)
@LazyToOne(LazyToOneOption.NO_PROXY)
private PhoneDetails details;

//Getters and setters are omitted for brevity
Expand Down
41 changes: 41 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/Hibernate.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,47 @@
* are intended for use by generic code that must materialize an "amputated" graph of
* Hibernate entities. (For example, a library which deserializes entities from JSON.)
* <p>
* Lazy fetching of a {@linkplain jakarta.persistence.OneToOne one to one} or
* {@linkplain jakarta.persistence.ManyToOne many to one} association requires special
* bytecode tricks. The tricks used depend on whether build-time bytecode enhancement
* is enabled.
* <p>
* When bytecode enhancement is <em>not</em> used, an unfetched lazy association* is
* represented by a <em>proxy object</em> which holds the identifier (foreign key) of
* the associated entity instance.
* <ul>
* <li>The identifier property of the proxy object is set when the proxy is instantiated.
* The program may obtain the entity identifier value of an unfetched proxy, without
* triggering lazy fetching, by calling the corresponding getter method.
* (It's even possible to set an association to reference an unfetched proxy.)
* <li>A delegate entity instance is lazily fetched when any other method of the proxy
* is called. Once fetched, the proxy delegates all method invocations to the
* delegate.
* <li>The proxy does not have the same concrete type as the proxied delegate, and so
* {@link #getClass(Object)} must be used in place of {@link Object#getClass()},
* and this method fetches the entity by side-effect.
* <li>For a polymorphic association, the concrete type of the associated entity is
* not known until the delegate is fetched from the database, and so
* {@link #unproxy(Object, Class)}} must be used to perform typecasts, and
* {@link #getClass(Object)} must be used instead of the Java {@code instanceof}
* operator.
* </ul>
* When bytecode enhancement <em>is</em> used, there is no such indirection, but the
* associated entity instance is initially in an unloaded state, with only its
* identifier field set.
* <ul>
* <li>The identifier field of an unloaded entity instance is set when the unloaded
* instance is instantiated. The program may obtain the identifier of an unloaded
* entity, without triggering lazy loading, by accessing the field containing the
* identifier.
* <li>The remaining non-lazy state of the entity instance is loaded lazily when any
* other field is accessed.
* <li>Typecasts, the Java {@code instanceof} operator, and {@link Object#getClass()}
* may be used as normal.
* </ul>
* As an exception to the above rules, <em>polymorphic</em> associations always work
* as if bytecode enhancement was not enabled.
*<p>
* Graphs of Hibernate entities obtained from a {@link Session} are usually in an
* amputated form, with associations and collections replaced by proxies and lazy
* collections. (That is, by instances of the internal types {@link HibernateProxy}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,33 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collection;
import java.util.Map;

/**
* Specify the laziness of a collection, either a
* {@link jakarta.persistence.OneToMany} or
* {@link jakarta.persistence.ManyToMany} association,
* or an {@link jakarta.persistence.ElementCollection}.
* <p>
* This is an alternative to specifying the JPA
* {@link jakarta.persistence.FetchType}.
* {@link jakarta.persistence.FetchType}. This annotation
* is used to enable {@linkplain LazyCollectionOption#EXTRA
* extra-lazy collection fetching}.
*
* @author Emmanuel Bernard
*
* @deprecated
* <ul>
* <li>Use the JPA-defined {@link jakarta.persistence.FetchType#EAGER}
* instead of {@code LazyCollection(FALSE)}.
* <li>Use static methods of {@link org.hibernate.Hibernate},
* for example {@link org.hibernate.Hibernate#size(Collection)},
* {@link org.hibernate.Hibernate#contains(Collection, Object)},
* or {@link org.hibernate.Hibernate#get(Map, Object)} instead
* of {@code LazyCollection(EXTRA)}.
* </ul>
*/
@Deprecated(since="6.2")
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LazyCollection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,54 @@
*/
package org.hibernate.annotations;

import java.util.Collection;
import java.util.Map;

/**
* Lazy options available for a collection.
* Enumerates the options for lazy loading of a
* {@linkplain jakarta.persistence.ElementCollection collection},
* {@linkplain jakarta.persistence.ManyToOne many to one association},
* or {@linkplain jakarta.persistence.ManyToMany many to many association}.
*
* @author Emmanuel Bernard
*
* @see LazyCollection
*
* @deprecated
* <ul>
* <li>Use the JPA-defined {@link jakarta.persistence.FetchType#EAGER}
* instead of {@code LazyCollection(FALSE)}.
* <li>Use static methods of {@link org.hibernate.Hibernate},
* for example {@link org.hibernate.Hibernate#size(Collection)},
* {@link org.hibernate.Hibernate#contains(Collection, Object)},
* or {@link org.hibernate.Hibernate#get(Map, Object)} instead
* of {@code LazyCollection(EXTRA)}.
* </ul>
*/
@Deprecated
public enum LazyCollectionOption {
/**
* Eagerly load it.
* The collection is always loaded eagerly, and all its
* elements are available immediately. However, access to
* the collection is still mediated by an instance of
* {@link org.hibernate.collection.spi.PersistentCollection},
* which tracks modifications to the collection.
*/
FALSE,
/**
* Load it when the state is requested.
* The underlying Java collection is proxied by an instance of
* {@link org.hibernate.collection.spi.PersistentCollection}
* and lazily fetched when a method of the proxy is called.
* All elements of the collection are retrieved at once.
*/
TRUE,
/**
* Prefer extra queries over full collection loading.
* The underlying Java collection is proxied by an instance of
* {@link org.hibernate.collection.spi.PersistentCollection}
* and its state is fetched lazily from the database as needed,
* when methods of the proxy are called. When reasonable, the
* proxy will avoid fetching all elements of the collection
* at once.
*/
EXTRA
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,16 @@
/**
* Specifies the machinery used to handle lazy fetching of
* the annotated {@link jakarta.persistence.OneToOne} or
* {@link jakarta.persistence.ManyToOne} association. This
* is an alternative to specifying only the JPA
* {@link jakarta.persistence.FetchType}. This annotation
* is occasionally useful, since there are observable
* differences in semantics between:
* <ul>
* <li>{@linkplain LazyToOneOption#FALSE eager fetching},
* <li>lazy fetching via interception of calls on a
* {@linkplain LazyToOneOption#PROXY proxy object},
* and
* <li>lazy fetching via interception of field access on
* the {@linkplain LazyToOneOption#NO_PROXY owning side}
* of the association.
* </ul>
* By default, an unfetched lazy association is represented
* by a <em>proxy object</em> which holds the identifier
* (foreign key) of the associated entity instance.
* It's possible to obtain the identifier from an unfetched
* proxy, without fetching the entity from the database, by
* calling the corresponding getter method. (It's even
* possible to set an association to reference an unfetched
* proxy.) Lazy fetching occurs when any other method of the
* proxy is called. Once fetched, the proxy delegates all
* method invocations to the fetched entity instance.
* For a polymorphic association, the concrete type of the
* entity instance represented by a proxy is unknown, and
* so {@link org.hibernate.Hibernate#getClass(Object)} must
* be used to obtain the concrete type, fetching the entity
* by side effect. Similarly, typecasts must be performed
* using {@link org.hibernate.Hibernate#unproxy(Object, Class)}.
* <p>
* With {@code LazyToOne(NO_PROXY)}, an associated entity
* instance begins in an unloaded state, with only its
* identifier field set. Thus, it's possible to obtain the
* identifier if an unloaded entity, without triggering
* lazy loading. Typecasts, {@code instanceof}, and
* {@link Object#getClass()} work as normal. But this
* option is only available when bytecode enhancement is
* used.
* <p>
* <strong>Currently, Hibernate does not support
* {@code LazyToOne(NO_PROXY)} for polymorphic associations,
* and instead falls back to using a proxy!</strong>
* {@link jakarta.persistence.ManyToOne} association.
* This is an alternative to specifying only the JPA
* {@link jakarta.persistence.FetchType}.
*
* @author Emmanuel Bernard
*
* @deprecated use JPA annotations to specify the
* {@link jakarta.persistence.FetchType}
*/
@Deprecated(since="6.2")
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LazyToOne {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@
* @author Emmanuel Bernard
*
* @see LazyToOne
*
* @deprecated since {@link LazyToOne} is deprecated, use
* {@link jakarta.persistence.FetchType} instead
*/
@Deprecated(since="6.2")
public enum LazyToOneOption {
/**
* The association is always loaded eagerly. The identifier
* and concrete type of the associated entity instance,
* along with all the rest of its non-lazy fields, are always
* available immediately.
*
* @deprecated use {@link jakarta.persistence.FetchType#EAGER}
*/
@Deprecated
FALSE,
/**
* The association is proxied and a delegate entity instance
Expand All @@ -38,7 +45,8 @@ public enum LazyToOneOption {
* <li>The proxy does not have the same concrete type as the
* proxied delegate, and so
* {@link org.hibernate.Hibernate#getClass(Object)}
* must be used in place of {@link Object#getClass()}.
* must be used in place of {@link Object#getClass()},
* and this method fetches the entity by side-effect.
* <li>For a polymorphic association, the concrete type of
* the proxied entity instance is not known until the
* delegate is fetched from the database, and so
Expand All @@ -48,7 +56,10 @@ public enum LazyToOneOption {
* must be used instead of the Java {@code instanceof}
* operator.
* </ul>
*
* @deprecated use {@link jakarta.persistence.FetchType#LAZY}
*/
@Deprecated
PROXY,
/**
* The associated entity instance is initially in an unloaded
Expand All @@ -65,9 +76,11 @@ public enum LazyToOneOption {
* <li>Bytecode enhancement is required. If the class is not
* enhanced, this option is equivalent to {@link #PROXY}.
* </ul>
* <strong>Currently, Hibernate does not support this setting
* for polymorphic associations, and instead falls back to
* {@link #PROXY}!</strong>
* Hibernate does not support this setting for polymorphic
* associations, and instead falls back to {@link #PROXY}.
*
* @deprecated this setting no longer has any useful effect
*/
@Deprecated
NO_PROXY
}
15 changes: 9 additions & 6 deletions hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -1014,11 +1014,7 @@ public String getGeneratedValueGeneratorName() {
}

private static GenerationType interpretGenerationType(GeneratedValue generatedValueAnn) {
if ( generatedValueAnn.strategy() == null ) {
return GenerationType.AUTO;
}

return generatedValueAnn.strategy();
return generatedValueAnn.strategy() == null ? GenerationType.AUTO : generatedValueAnn.strategy();
}

public static boolean isEmptyAnnotationValue(String annotationString) {
Expand Down Expand Up @@ -1265,7 +1261,14 @@ public static <T extends Annotation> T getOverridableAnnotation(
}

public static FetchMode getFetchMode(FetchType fetch) {
return fetch == FetchType.EAGER ? FetchMode.JOIN : FetchMode.SELECT;
switch ( fetch ) {
case EAGER:
return FetchMode.JOIN;
case LAZY:
return FetchMode.SELECT;
default:
throw new AssertionFailure("unknown fetch type: " + fetch);
}
}

private static CascadeType convertCascadeType(jakarta.persistence.CascadeType cascade) {
Expand Down

0 comments on commit 5b5721f

Please sign in to comment.