diff --git a/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc b/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
index 4565ed821f..1094ace848 100644
--- a/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
+++ b/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
@@ -432,17 +432,16 @@ is accomplished using the `org.hibernate.annotation.NotFound` annotation with a
====
Not enforcing physical foreign-keys is very discouraged.
-`@ManyToOne` and `@OneToOne` associations annotated with `@NotFound(action = NotFoundAction.IGNORE)` are
+`@ManyToOne` and `@OneToOne` associations annotated with `@NotFound` are
always fetched eagerly even if the `fetch` strategy is set to `FetchType.LAZY`.
It also affects how the association are treated as "implicit joins" in HQL. Normally
-Hibernate would use INNER joins. With `NotFoundAction.IGNORE`, a LEFT join is
-used instead.
+Hibernate would use INNER joins. With `@NotFound`, a LEFT join is used instead.
It also forces a join when Hibernate normally would not. Consider the HQL `.. where root.notFoundAssoc.id`.
Hibernate will normally use the foreign-key "referring" column(s) which does not require
a join. It does this because a physical foreign-key would prevent this column to refer to a non-existent
-associated primary-key. With `NotFoundAction.IGNORE`, we need to look at the foreign-key "target" column(s)
+associated primary-key. With `@NotFound`, we need to look at the foreign-key "target" column(s)
which requires the join.
====
diff --git a/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java b/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java
new file mode 100644
index 0000000000..364a6addc7
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java
@@ -0,0 +1,43 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate;
+
+import java.util.Locale;
+import jakarta.persistence.EntityNotFoundException;
+
+/**
+ * Exception for {@link org.hibernate.annotations.NotFoundAction#EXCEPTION}
+ *
+ * @see org.hibernate.annotations.NotFound
+ *
+ * @author Steve Ebersole
+ */
+public class FetchNotFoundException extends EntityNotFoundException {
+ private final String entityName;
+ private final Object identifier;
+
+ public FetchNotFoundException(String entityName, Object identifier) {
+ super(
+ String.format(
+ Locale.ROOT,
+ "Entity `%s` with identifier value `%s` does not exist",
+ entityName,
+ identifier
+ )
+ );
+ this.entityName = entityName;
+ this.identifier = identifier;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public Object getIdentifier() {
+ return identifier;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java b/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
index 99afb9a367..5ea41bd673 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
@@ -7,27 +7,38 @@
package org.hibernate.annotations;
/**
- * Enumerates the association fetching strategies available in Hibernate.
- *
- * Whereas the JPA {@link jakarta.persistence.FetchType} enumeration provides a way to
- * specify when an association should be fetched, this enumeration provides a
- * way to express how it should be fetched.
+ * How the association should be fetched.
*
+ * Defines the "how", compared to {@link jakarta.persistence.FetchType} which defines "when"
+ *
+ * @author Steve Ebersole
* @author Emmanuel Bernard
*/
public enum FetchMode {
/**
- * The association or collection is fetched with a separate subsequent SQL select.
+ * Use a secondary select for each individual entity, collection, or join load.
*/
- SELECT,
+ SELECT( org.hibernate.FetchMode.SELECT ),
/**
- * The association or collection is fetched using an outer join clause added to
- * the initial SQL select.
+ * Use an outer join to load the related entities, collections or joins.
*/
- JOIN,
+ JOIN( org.hibernate.FetchMode.JOIN ),
/**
- * For collections and many-valued associations only. After the initial SQL select,
- * all associated collections are fetched together in a single subsequent select.
+ * Available for collections only.
+ *
+ * When accessing a non-initialized collection, this fetch mode will trigger
+ * loading all elements of all collections of the same role for all owners
+ * associated with the persistence context using a single secondary select.
*/
- SUBSELECT
+ SUBSELECT( org.hibernate.FetchMode.SELECT );
+
+ private final org.hibernate.FetchMode hibernateFetchMode;
+
+ FetchMode(org.hibernate.FetchMode hibernateFetchMode) {
+ this.hibernateFetchMode = hibernateFetchMode;
+ }
+
+ public org.hibernate.FetchMode getHibernateFetchMode() {
+ return hibernateFetchMode;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
index 8392cda2af..389d7d80d2 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
@@ -2415,7 +2415,7 @@ public final class AnnotationBinder {
collectionBinder.setPropertyHolder(propertyHolder);
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
- collectionBinder.setIgnoreNotFound( notFound != null && notFound.action() == NotFoundAction.IGNORE );
+ collectionBinder.setNotFoundAction( notFound == null ? null : notFound.action() );
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
@@ -2652,7 +2652,8 @@ public final class AnnotationBinder {
|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
- boolean ignoreNotFound = notFound != null && notFound.action() == NotFoundAction.IGNORE;
+ NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
+
// MapsId means the columns belong to the pk;
// A @MapsId association (obviously) must be non-null when the entity is first persisted.
// If a @MapsId association is not mapped with @NotFound(IGNORE), then the association
@@ -2662,8 +2663,8 @@ public final class AnnotationBinder {
// @OneToOne(optional = true) with @PKJC makes the association optional.
final boolean mandatory = !ann.optional()
|| property.isAnnotationPresent( Id.class )
- || property.isAnnotationPresent( MapsId.class ) && !ignoreNotFound;
- matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), ignoreNotFound, ann.fetch() );
+ || property.isAnnotationPresent( MapsId.class ) && notFoundAction != NotFoundAction.IGNORE;
+ matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, ann.fetch() );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
JoinTable assocTable = propertyHolder.getJoinTable(property);
if ( assocTable != null ) {
@@ -2677,7 +2678,7 @@ public final class AnnotationBinder {
joinColumns,
!mandatory,
getFetchMode( ann.fetch() ),
- ignoreNotFound,
+ notFoundAction,
onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action(),
ToOneBinder.getTargetEntity(inferredData, context),
propertyHolder,
@@ -2714,8 +2715,8 @@ public final class AnnotationBinder {
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
- boolean ignoreNotFound = notFound != null && notFound.action() == NotFoundAction.IGNORE;
- matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), ignoreNotFound, ann.fetch() );
+ NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
+ matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, ann.fetch() );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
JoinTable assocTable = propertyHolder.getJoinTable(property);
if ( assocTable != null ) {
@@ -2732,12 +2733,12 @@ public final class AnnotationBinder {
// the association is optional.
final boolean mandatory = !ann.optional()
|| property.isAnnotationPresent( Id.class )
- || property.isAnnotationPresent( MapsId.class ) && !ignoreNotFound;
+ || property.isAnnotationPresent( MapsId.class ) && notFoundAction != null;
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false, forcePersist),
joinColumns,
!mandatory,
- ignoreNotFound,
+ notFoundAction,
onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action(),
ToOneBinder.getTargetEntity(inferredData, context),
propertyHolder,
@@ -3545,7 +3546,7 @@ public final class AnnotationBinder {
String cascadeStrategy,
AnnotatedJoinColumn[] columns,
boolean optional,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
@@ -3565,7 +3566,7 @@ public final class AnnotationBinder {
final XProperty property = inferredData.getProperty();
defineFetchingStrategy( value, property );
//value.setFetchMode( fetchMode );
- value.setIgnoreNotFound( ignoreNotFound );
+ value.setNotFoundAction( notFoundAction );
value.setCascadeDeleteEnabled( cascadeOnDelete );
//value.setLazy( fetchMode != FetchMode.JOIN );
if ( !optional ) {
@@ -3671,6 +3672,8 @@ public final class AnnotationBinder {
Fetch fetch = property.getAnnotation( Fetch.class );
ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
OneToOne oneToOne = property.getAnnotation( OneToOne.class );
+ NotFound notFound = property.getAnnotation( NotFound.class );
+
FetchType fetchType;
if ( manyToOne != null ) {
fetchType = manyToOne.fetch();
@@ -3683,7 +3686,12 @@ public final class AnnotationBinder {
"Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne"
);
}
- if ( lazy != null ) {
+
+ if ( notFound != null ) {
+ toOne.setLazy( false );
+ toOne.setUnwrapProxy( true );
+ }
+ else if ( lazy != null ) {
toOne.setLazy( !( lazy.value() == LazyToOneOption.FALSE ) );
toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) );
}
@@ -3692,6 +3700,7 @@ public final class AnnotationBinder {
toOne.setUnwrapProxy( fetchType != FetchType.LAZY );
toOne.setUnwrapProxyImplicit( true );
}
+
if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
toOne.setFetchMode( FetchMode.JOIN );
@@ -3718,7 +3727,7 @@ public final class AnnotationBinder {
AnnotatedJoinColumn[] joinColumns,
boolean optional,
FetchMode fetchMode,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
@@ -3769,7 +3778,7 @@ public final class AnnotationBinder {
propertyHolder,
inferredData,
targetEntity,
- ignoreNotFound,
+ notFoundAction,
cascadeOnDelete,
optional,
cascadeStrategy,
@@ -3789,7 +3798,7 @@ public final class AnnotationBinder {
else {
//has a FK on the table
bindManyToOne(
- cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
+ cascadeStrategy, joinColumns, optional, notFoundAction, cascadeOnDelete,
targetEntity,
propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass,
propertyBinder, context
@@ -4137,9 +4146,9 @@ public final class AnnotationBinder {
private static void matchIgnoreNotFoundWithFetchType(
String entity,
String association,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
FetchType fetchType) {
- if ( ignoreNotFound && fetchType == FetchType.LAZY ) {
+ if ( notFoundAction != null && fetchType == FetchType.LAZY ) {
LOG.ignoreNotFoundWithFetchTypeLazy( entity, association );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
index 7b4982ee20..d50ea9ea22 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
@@ -14,6 +14,7 @@ import jakarta.persistence.JoinColumns;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.LazyGroup;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.PropertyBinder;
@@ -39,7 +40,7 @@ public class OneToOneSecondPass implements SecondPass {
private final String ownerEntity;
private final String ownerProperty;
private final PropertyHolder propertyHolder;
- private final boolean ignoreNotFound;
+ private final NotFoundAction notFoundAction;
private final PropertyData inferredData;
private final XClass targetEntity;
private final boolean cascadeOnDelete;
@@ -55,7 +56,7 @@ public class OneToOneSecondPass implements SecondPass {
PropertyHolder propertyHolder,
PropertyData inferredData,
XClass targetEntity,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
boolean optional,
String cascadeStrategy,
@@ -66,7 +67,7 @@ public class OneToOneSecondPass implements SecondPass {
this.mappedBy = mappedBy;
this.propertyHolder = propertyHolder;
this.buildingContext = buildingContext;
- this.ignoreNotFound = ignoreNotFound;
+ this.notFoundAction = notFoundAction;
this.inferredData = inferredData;
this.targetEntity = targetEntity;
this.cascadeOnDelete = cascadeOnDelete;
@@ -187,7 +188,7 @@ public class OneToOneSecondPass implements SecondPass {
);
ManyToOne manyToOne = new ManyToOne( buildingContext, mappedByJoin.getTable() );
//FIXME use ignore not found here
- manyToOne.setIgnoreNotFound( ignoreNotFound );
+ manyToOne.setNotFoundAction( notFoundAction );
manyToOne.setCascadeDeleteEnabled( value.isCascadeDeleteEnabled() );
manyToOne.setFetchMode( value.getFetchMode() );
manyToOne.setLazy( value.isLazy() );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
index d100e19fd0..f7583e33e2 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
@@ -13,6 +13,22 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.function.Supplier;
+import jakarta.persistence.Access;
+import jakarta.persistence.AttributeOverride;
+import jakarta.persistence.AttributeOverrides;
+import jakarta.persistence.CollectionTable;
+import jakarta.persistence.ConstraintMode;
+import jakarta.persistence.ElementCollection;
+import jakarta.persistence.Embeddable;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.JoinColumns;
+import jakarta.persistence.JoinTable;
+import jakarta.persistence.ManyToMany;
+import jakarta.persistence.MapKey;
+import jakarta.persistence.MapKeyColumn;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.OrderColumn;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
@@ -44,7 +60,8 @@ import org.hibernate.annotations.ListIndexJdbcType;
import org.hibernate.annotations.ListIndexJdbcTypeCode;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.ManyToAny;
-import org.hibernate.annotations.MapKeyCustomCompositeType;
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OptimisticLock;
@@ -115,23 +132,6 @@ import org.hibernate.usertype.UserCollectionType;
import org.jboss.logging.Logger;
-import jakarta.persistence.Access;
-import jakarta.persistence.AttributeOverride;
-import jakarta.persistence.AttributeOverrides;
-import jakarta.persistence.CollectionTable;
-import jakarta.persistence.ConstraintMode;
-import jakarta.persistence.ElementCollection;
-import jakarta.persistence.Embeddable;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.JoinColumns;
-import jakarta.persistence.JoinTable;
-import jakarta.persistence.ManyToMany;
-import jakarta.persistence.MapKey;
-import jakarta.persistence.MapKeyColumn;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.OrderColumn;
-
import static jakarta.persistence.AccessType.PROPERTY;
import static org.hibernate.cfg.AnnotatedColumn.checkPropertyConsistency;
import static org.hibernate.cfg.AnnotationBinder.fillComponent;
@@ -186,7 +186,7 @@ public abstract class CollectionBinder {
private AnnotatedColumn[] elementColumns;
private boolean isEmbedded;
private XProperty property;
- private boolean ignoreNotFound;
+ private NotFoundAction notFoundAction;
private TableBinder tableBinder;
private AnnotatedColumn[] mapKeyColumns;
private AnnotatedJoinColumn[] mapKeyManyToManyColumns;
@@ -719,7 +719,7 @@ public abstract class CollectionBinder {
isEmbedded,
property,
collectionType,
- ignoreNotFound,
+ notFoundAction,
oneToMany,
tableBinder,
buildingContext
@@ -970,6 +970,8 @@ public abstract class CollectionBinder {
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
ElementCollection elementCollection = property.getAnnotation( ElementCollection.class );
ManyToAny manyToAny = property.getAnnotation( ManyToAny.class );
+ NotFound notFound = property.getAnnotation( NotFound.class );
+
FetchType fetchType;
if ( oneToMany != null ) {
fetchType = oneToMany.fetch();
@@ -985,36 +987,58 @@ public abstract class CollectionBinder {
}
else {
throw new AssertionFailure(
- "Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @ElementCollection"
+ "Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @CollectionOfElements"
);
}
- if ( lazy != null ) {
- collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
- collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
- }
- else {
- collection.setLazy( fetchType == FetchType.LAZY );
- collection.setExtraLazy( false );
- }
- if ( fetch != null ) {
- if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
- collection.setFetchMode( FetchMode.JOIN );
- collection.setLazy( false );
+ if ( notFound != null ) {
+ collection.setLazy( false );
+
+ if ( lazy != null ) {
+ collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
}
- else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
- collection.setFetchMode( FetchMode.SELECT );
- }
- else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
- collection.setFetchMode( FetchMode.SELECT );
- collection.setSubselectLoadable( true );
- collection.getOwner().setSubselectLoadableCollections( true );
+
+ if ( fetch != null ) {
+ if ( fetch.value() != null ) {
+ collection.setFetchMode( fetch.value().getHibernateFetchMode() );
+ if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
+ collection.setSubselectLoadable( true );
+ collection.getOwner().setSubselectLoadableCollections( true );
+ }
+ }
}
else {
- throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
+ collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
}
}
else {
- collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
+ if ( lazy != null ) {
+ collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
+ collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
+ }
+ else {
+ collection.setLazy( fetchType == FetchType.LAZY );
+ collection.setExtraLazy( false );
+ }
+ if ( fetch != null ) {
+ if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
+ collection.setFetchMode( FetchMode.JOIN );
+ collection.setLazy( false );
+ }
+ else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
+ collection.setFetchMode( FetchMode.SELECT );
+ }
+ else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
+ collection.setFetchMode( FetchMode.SELECT );
+ collection.setSubselectLoadable( true );
+ collection.getOwner().setSubselectLoadableCollections( true );
+ }
+ else {
+ throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
+ }
+ }
+ else {
+ collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
+ }
}
}
@@ -1044,7 +1068,7 @@ public abstract class CollectionBinder {
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
@@ -1062,7 +1086,7 @@ public abstract class CollectionBinder {
property,
unique,
assocTableBinder,
- ignoreNotFound,
+ notFoundAction,
buildingContext
);
}
@@ -1083,7 +1107,7 @@ public abstract class CollectionBinder {
XProperty property,
boolean unique,
TableBinder associationTableBinder,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
PersistentClass persistentClass = persistentClasses.get( collType.getName() );
boolean reversePropertyInJoin = false;
@@ -1118,7 +1142,7 @@ public abstract class CollectionBinder {
fkJoinColumns,
collType,
cascadeDeleteEnabled,
- ignoreNotFound,
+ notFoundAction,
buildingContext,
inheritanceStatePerClass
);
@@ -1132,8 +1156,10 @@ public abstract class CollectionBinder {
keyColumns,
inverseColumns,
elementColumns,
- isEmbedded, collType,
- ignoreNotFound, unique,
+ isEmbedded,
+ collType,
+ notFoundAction,
+ unique,
cascadeDeleteEnabled,
associationTableBinder,
property,
@@ -1150,7 +1176,7 @@ public abstract class CollectionBinder {
AnnotatedJoinColumn[] fkJoinColumns,
XClass collectionType,
boolean cascadeDeleteEnabled,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext,
Map inheritanceStatePerClass) {
@@ -1166,7 +1192,7 @@ public abstract class CollectionBinder {
new org.hibernate.mapping.OneToMany( buildingContext, collection.getOwner() );
collection.setElement( oneToMany );
oneToMany.setReferencedEntityName( collectionType.getName() );
- oneToMany.setIgnoreNotFound( ignoreNotFound );
+ oneToMany.setNotFoundAction( notFoundAction );
String assocClass = oneToMany.getReferencedEntityName();
PersistentClass associatedClass = persistentClasses.get( assocClass );
@@ -1589,7 +1615,8 @@ public abstract class CollectionBinder {
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XClass collType,
- boolean ignoreNotFound, boolean unique,
+ NotFoundAction notFoundAction,
+ boolean unique,
boolean cascadeDeleteEnabled,
TableBinder associationTableBinder,
XProperty property,
@@ -1656,7 +1683,7 @@ public abstract class CollectionBinder {
element = handleCollectionOfEntities(
collValue,
collType,
- ignoreNotFound,
+ notFoundAction,
property,
buildingContext,
collectionEntity,
@@ -1841,7 +1868,14 @@ public abstract class CollectionBinder {
}
}
- private ManyToOne handleCollectionOfEntities(Collection collValue, XClass collType, boolean ignoreNotFound, XProperty property, MetadataBuildingContext buildingContext, PersistentClass collectionEntity, String hqlOrderBy) {
+ private ManyToOne handleCollectionOfEntities(
+ Collection collValue,
+ XClass collType,
+ NotFoundAction notFoundAction,
+ XProperty property,
+ MetadataBuildingContext buildingContext,
+ PersistentClass collectionEntity,
+ String hqlOrderBy) {
ManyToOne element;
element = new ManyToOne(buildingContext, collValue.getCollectionTable() );
collValue.setElement( element );
@@ -1851,7 +1885,7 @@ public abstract class CollectionBinder {
//make the second join non lazy
element.setFetchMode( FetchMode.JOIN );
element.setLazy( false );
- element.setIgnoreNotFound(ignoreNotFound);
+ element.setNotFoundAction( notFoundAction );
// as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
if ( hqlOrderBy != null ) {
collValue.setManyToManyOrdering(
@@ -2276,8 +2310,18 @@ public abstract class CollectionBinder {
this.property = property;
}
+ public NotFoundAction getNotFoundAction() {
+ return notFoundAction;
+ }
+
+ public void setNotFoundAction(NotFoundAction notFoundAction) {
+ this.notFoundAction = notFoundAction;
+ }
+
public void setIgnoreNotFound(boolean ignoreNotFound) {
- this.ignoreNotFound = ignoreNotFound;
+ this.notFoundAction = ignoreNotFound
+ ? NotFoundAction.IGNORE
+ : null;
}
public void setMapKeyColumns(AnnotatedColumn[] mapKeyColumns) {
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
index 5dec409b01..cf7ccd1d57 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
@@ -12,6 +12,7 @@ import java.util.function.Supplier;
import org.hibernate.MappingException;
import org.hibernate.annotations.CollectionId;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
@@ -61,7 +62,7 @@ public class IdBagBinder extends BagBinder {
XProperty property,
boolean unique,
TableBinder associationTableBinder,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
boolean result = super.bindStarToManySecondPass(
persistentClasses,
@@ -74,7 +75,7 @@ public class IdBagBinder extends BagBinder {
property,
unique,
associationTableBinder,
- ignoreNotFound,
+ notFoundAction,
getBuildingContext()
);
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
index 8453956fec..d89bee6e8d 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
@@ -10,6 +10,7 @@ import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
@@ -71,7 +72,7 @@ public class ListBinder extends CollectionBinder {
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
@@ -90,7 +91,7 @@ public class ListBinder extends CollectionBinder {
property,
unique,
assocTableBinder,
- ignoreNotFound,
+ notFoundAction,
buildingContext
);
bindIndex( property, collType, buildingContext );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
index f907721759..cabe544b9b 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
@@ -16,6 +16,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.MapKeyCustomCompositeType;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.BootstrapContext;
@@ -96,7 +97,7 @@ public class MapBinder extends CollectionBinder {
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
@@ -105,7 +106,7 @@ public class MapBinder extends CollectionBinder {
throws MappingException {
bindStarToManySecondPass(
persistentClasses, collType, fkJoinColumns, keyColumns, inverseColumns, elementColumns,
- isEmbedded, property, unique, assocTableBinder, ignoreNotFound, buildingContext
+ isEmbedded, property, unique, assocTableBinder, notFoundAction, buildingContext
);
bindKeyFromAssociationTable(
collType, persistentClasses, mapKeyPropertyName, property, isEmbedded, buildingContext,
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
index 82ff20e1a1..087d71bc9d 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
@@ -1715,8 +1715,8 @@ public interface CoreMessageLogger extends BasicLogger {
void usingJtaPlatform(String jtaPlatformClassName);
@LogMessage(level = WARN)
- @Message(value = "The [%2$s] association in the [%1$s] entity uses both @NotFound(action = NotFoundAction.IGNORE) and FetchType.LAZY. " +
- "The NotFoundAction.IGNORE @ManyToOne and @OneToOne associations are always fetched eagerly.", id = 491)
+ @Message(value = "`.%1$s.%2$s` uses both @NotFound and FetchType.LAZY. @ManyToOne and " +
+ "@OneToOne associations mapped with `@NotFound` are forced to EAGER fetching.", id = 491)
void ignoreNotFoundWithFetchTypeLazy(String entity, String association);
@LogMessage(level = INFO)
diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java
index d129573954..6cc05a5f87 100644
--- a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java
+++ b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java
@@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.Map;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
@@ -19,8 +20,8 @@ import org.hibernate.type.Type;
* @author Gavin King
*/
public class ManyToOne extends ToOne {
- private boolean ignoreNotFound;
private boolean isLogicalOneToOne;
+ private NotFoundAction notFoundAction;
private Type resolvedType;
@@ -30,7 +31,7 @@ public class ManyToOne extends ToOne {
private ManyToOne(ManyToOne original) {
super( original );
- this.ignoreNotFound = original.ignoreNotFound;
+ this.notFoundAction = original.notFoundAction;
this.isLogicalOneToOne = original.isLogicalOneToOne;
}
@@ -113,12 +114,22 @@ public class ManyToOne extends ToOne {
return visitor.accept(this);
}
+ public NotFoundAction getNotFoundAction() {
+ return notFoundAction;
+ }
+
+ public void setNotFoundAction(NotFoundAction notFoundAction) {
+ this.notFoundAction = notFoundAction;
+ }
+
public boolean isIgnoreNotFound() {
- return ignoreNotFound;
+ return notFoundAction == NotFoundAction.IGNORE;
}
public void setIgnoreNotFound(boolean ignoreNotFound) {
- this.ignoreNotFound = ignoreNotFound;
+ this.notFoundAction = ignoreNotFound
+ ? NotFoundAction.IGNORE
+ : null;
}
public void markAsLogicalOneToOne() {
diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java
index 509ab8e06b..b9753e242f 100644
--- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java
+++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java
@@ -12,6 +12,7 @@ import java.util.Objects;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.service.ServiceRegistry;
@@ -29,7 +30,7 @@ public class OneToMany implements Value {
private String referencedEntityName;
private PersistentClass associatedClass;
- private boolean ignoreNotFound;
+ private NotFoundAction notFoundAction;
public OneToMany(MetadataBuildingContext buildingContext, PersistentClass owner) throws MappingException {
this.buildingContext = buildingContext;
@@ -41,7 +42,7 @@ public class OneToMany implements Value {
this.referencingTable = original.referencingTable;
this.referencedEntityName = original.referencedEntityName;
this.associatedClass = original.associatedClass;
- this.ignoreNotFound = original.ignoreNotFound;
+ this.notFoundAction = original.notFoundAction;
}
@Override
@@ -197,12 +198,22 @@ public class OneToMany implements Value {
return false;
}
+ public NotFoundAction getNotFoundAction() {
+ return notFoundAction;
+ }
+
+ public void setNotFoundAction(NotFoundAction notFoundAction) {
+ this.notFoundAction = notFoundAction;
+ }
+
public boolean isIgnoreNotFound() {
- return ignoreNotFound;
+ return notFoundAction == NotFoundAction.IGNORE;
}
public void setIgnoreNotFound(boolean ignoreNotFound) {
- this.ignoreNotFound = ignoreNotFound;
+ this.notFoundAction = ignoreNotFound
+ ? NotFoundAction.IGNORE
+ : null;
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java
index 47320a93c7..8c00aeeb9d 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java
@@ -277,6 +277,7 @@ public class BasicAttributeMapping
getContainingTableExpression(),
allowFkOptimization
);
+
return expressionResolver.resolveSqlSelection(
expressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java
index 723cb99438..865e886648 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java
@@ -13,6 +13,7 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
@@ -82,6 +83,7 @@ public class EntityCollectionPart
private final Nature nature;
private final EntityMappingType entityMappingType;
private final Set targetKeyPropertyNames;
+ private final NotFoundAction notFoundAction;
private ModelPart fkTargetModelPart;
private ForeignKeyDescriptor fkDescriptor;
@@ -90,12 +92,15 @@ public class EntityCollectionPart
CollectionPersister collectionDescriptor,
Nature nature,
Value bootModelValue,
+ NotFoundAction notFoundAction,
EntityMappingType entityMappingType,
MappingModelCreationProcess creationProcess) {
+ this.notFoundAction = notFoundAction;
this.navigableRole = collectionDescriptor.getNavigableRole().appendContainer( nature.getName() );
this.collectionDescriptor = collectionDescriptor;
this.nature = nature;
this.entityMappingType = entityMappingType;
+
final String referencedPropertyName;
final PersistentClass entityBinding;
if ( bootModelValue instanceof OneToMany ) {
@@ -112,6 +117,7 @@ public class EntityCollectionPart
entityBinding = toOne.getBuildingContext().getMetadataCollector()
.getEntityBinding( entityMappingType.getEntityName() );
}
+
if ( referencedPropertyName == null ) {
final Set targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
@@ -460,7 +466,8 @@ public class EntityCollectionPart
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
- final boolean added = creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
+ final ForeignKeyDescriptor foreignKeyDescriptor = getForeignKeyDescriptor();
+ final boolean added = creationState.registerVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() );
final TableGroup partTableGroup = resolveTableGroup( fetchablePath, creationState );
final EntityFetchJoinedImpl fetch = new EntityFetchJoinedImpl(
@@ -470,9 +477,11 @@ public class EntityCollectionPart
fetchablePath,
creationState
);
+
if ( added ) {
- creationState.removeVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
+ creationState.removeVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() );
}
+
return fetch;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java
index d162057c51..d97419eb72 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java
@@ -16,6 +16,7 @@ import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.collection.internal.StandardArraySemantics;
import org.hibernate.collection.internal.StandardBagSemantics;
@@ -1376,6 +1377,7 @@ public class MappingModelCreationHelper {
collectionDescriptor,
CollectionPart.Nature.INDEX,
bootMapKeyDescriptor,
+ null,
associatedEntity,
creationProcess
);
@@ -1473,10 +1475,22 @@ public class MappingModelCreationHelper {
final EntityType elementEntityType = (EntityType) collectionDescriptor.getElementType();
final EntityPersister associatedEntity = creationProcess.getEntityPersister( elementEntityType.getAssociatedEntityName() );
+ final NotFoundAction notFoundAction;
+ if ( element instanceof ManyToOne ) {
+ notFoundAction = ( (ManyToOne) element ).getNotFoundAction();
+ }
+ else if ( element instanceof OneToMany ) {
+ notFoundAction = ( (OneToMany) element ).getNotFoundAction();
+ }
+ else {
+ throw new IllegalArgumentException( "Just seeing if this happens" );
+ }
+
final EntityCollectionPart elementDescriptor = new EntityCollectionPart(
collectionDescriptor,
CollectionPart.Nature.ELEMENT,
bootDescriptor.getElement(),
+ notFoundAction,
associatedEntity,
creationProcess
);
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java
index 39eba32142..9d1936c27f 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java
@@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.IntFunction;
@@ -41,6 +42,7 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
+import org.hibernate.sql.ast.tree.from.UnknownTableReferenceException;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
@@ -230,10 +232,25 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
- final TableReference tableReference = tableGroup.resolveTableReference(
- navigablePath.append( getTargetPart().getFetchableName() ),
- selectableMapping.getContainingTableExpression()
- );
+ final TableReference tableReference;
+ try {
+ tableReference = tableGroup.resolveTableReference(
+ navigablePath.append( getTargetPart().getFetchableName() ),
+ selectableMapping.getContainingTableExpression()
+ );
+ }
+ catch (IllegalStateException tableNotFoundException) {
+ throw new UnknownTableReferenceException(
+ selectableMapping.getContainingTableExpression(),
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ selectableMapping.getContainingTableExpression(),
+ getNavigableRole().getFullPath()
+ )
+ );
+ }
+
final String identificationVariable = tableReference.getIdentificationVariable();
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java
index 24c64b8e31..3d61de1346 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java
@@ -15,6 +15,7 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.LockMode;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@@ -51,9 +52,9 @@ import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.PropertyAccess;
-import org.hibernate.query.sqm.spi.EntityIdentifierNavigablePath;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.spi.TreatedNavigablePath;
+import org.hibernate.query.sqm.spi.EntityIdentifierNavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess;
@@ -132,7 +133,7 @@ public class ToOneAttributeMapping
*/
private final boolean isKeyTableNullable;
private final boolean isConstrained;
- private final boolean isIgnoreNotFound;
+ private final NotFoundAction notFoundAction;
private final boolean unwrapProxy;
private final boolean isOptional;
private final EntityMappingType entityMappingType;
@@ -193,7 +194,7 @@ public class ToOneAttributeMapping
name,
stateArrayPosition,
attributeMetadataAccess,
- mappedFetchTiming,
+ adjustFetchTiming( mappedFetchTiming, bootValue ),
mappedFetchStyle,
declaringType,
propertyAccess,
@@ -208,7 +209,7 @@ public class ToOneAttributeMapping
if ( bootValue instanceof ManyToOne ) {
final ManyToOne manyToOne = (ManyToOne) bootValue;
- this.isIgnoreNotFound = ( (ManyToOne) bootValue ).isIgnoreNotFound();
+ this.notFoundAction = ( (ManyToOne) bootValue ).getNotFoundAction();
if ( manyToOne.isLogicalOneToOne() ) {
cardinality = Cardinality.LOGICAL_ONE_TO_ONE;
}
@@ -350,7 +351,7 @@ public class ToOneAttributeMapping
else {
this.bidirectionalAttributeName = bidirectionalAttributeName;
}
- isIgnoreNotFound = isNullable();
+ notFoundAction = isNullable() ? NotFoundAction.IGNORE : null;
isKeyTableNullable = isNullable();
isOptional = ! bootValue.isConstrained();
}
@@ -454,6 +455,15 @@ public class ToOneAttributeMapping
}
}
+ private static FetchTiming adjustFetchTiming(FetchTiming mappedFetchTiming, ToOne bootValue) {
+ if ( bootValue instanceof ManyToOne ) {
+ if ( ( (ManyToOne) bootValue ).getNotFoundAction() != null ) {
+ return FetchTiming.IMMEDIATE;
+ }
+ }
+ return mappedFetchTiming;
+ }
+
private TableGroupProducer resolveDeclaringTableGroupProducer(EntityPersister declaringEntityPersister) {
// Also handle cases where a collection contains an embeddable, that contains an association
NavigableRole parentRole = getNavigableRole().getParent();
@@ -497,7 +507,7 @@ public class ToOneAttributeMapping
this.isNullable = original.isNullable;
this.isKeyTableNullable = original.isKeyTableNullable;
this.isOptional = original.isOptional;
- this.isIgnoreNotFound = original.isIgnoreNotFound;
+ this.notFoundAction = original.notFoundAction;
this.unwrapProxy = original.unwrapProxy;
this.entityMappingType = original.entityMappingType;
this.referencedPropertyName = original.referencedPropertyName;
@@ -606,7 +616,8 @@ public class ToOneAttributeMapping
// We can only use the parent table group if the FK is located there and ignoreNotFound is false
// If this is not the case, the FK is not constrained or on a join/secondary table, so we need a join
- this.canUseParentTableGroup = !isIgnoreNotFound && sideNature == ForeignKeyDescriptor.Nature.KEY
+ this.canUseParentTableGroup = notFoundAction != NotFoundAction.IGNORE
+ && sideNature == ForeignKeyDescriptor.Nature.KEY
&& declaringTableGroupProducer.containsTableReference( identifyingColumnsTableExpression );
}
@@ -928,23 +939,24 @@ public class ToOneAttributeMapping
}
// The referencedNavigablePath can be null if this is a collection initialization
if ( referencedNavigablePath != null ) {
+ // If this is the key side, we must ensure that the key is not null, so we create a domain result for it
+ // In the CircularBiDirectionalFetchImpl we return null if the key is null instead of the bidirectional value
+ final DomainResult> keyDomainResult;
+ // For now, we don't do this if the key table is nullable to avoid an additional join
+ if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) {
+ keyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
+ fetchablePath,
+ creationState.getSqlAstCreationState()
+ .getFromClauseAccess()
+ .findTableGroup( realFetchParent.getNavigablePath() ),
+ creationState
+ );
+ }
+ else {
+ keyDomainResult = null;
+ }
+
if ( hasBidirectionalFetchParent ) {
- // If this is the key side, we must ensure that the key is not null, so we create a domain result for it
- // In the CircularBiDirectionalFetchImpl we return null if the key is null instead of the bidirectional value
- final DomainResult> keyDomainResult;
- // For now, we don't do this if the key table is nullable to avoid an additional join
- if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) {
- keyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
- fetchablePath,
- creationState.getSqlAstCreationState()
- .getFromClauseAccess()
- .findTableGroup( realFetchParent.getNavigablePath() ),
- creationState
- );
- }
- else {
- keyDomainResult = null;
- }
return new CircularBiDirectionalFetchImpl(
FetchTiming.IMMEDIATE,
fetchablePath,
@@ -968,6 +980,7 @@ public class ToOneAttributeMapping
fetchParent,
this,
tableGroup,
+ keyDomainResult,
fetchablePath,
creationState
);
@@ -1012,9 +1025,7 @@ public class ToOneAttributeMapping
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
- final TableGroup parentTableGroup = fromClauseAccess.getTableGroup(
- fetchParent.getNavigablePath()
- );
+ final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() );
final NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals( fetchParent.getNavigablePath() )
@@ -1072,10 +1083,33 @@ public class ToOneAttributeMapping
}
}
}
+
+ final DomainResult> keyResult;
+ if ( notFoundAction != null ) {
+ if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
+ keyResult = foreignKeyDescriptor.createKeyDomainResult(
+ fetchablePath,
+ parentTableGroup,
+ creationState
+ );
+ }
+ else {
+ keyResult = foreignKeyDescriptor.createTargetDomainResult(
+ fetchablePath,
+ parentTableGroup,
+ creationState
+ );
+ }
+ }
+ else {
+ keyResult = null;
+ }
+
final EntityFetchJoinedImpl entityFetchJoined = new EntityFetchJoinedImpl(
fetchParent,
this,
tableGroup,
+ keyResult,
fetchablePath,
creationState
);
@@ -1555,8 +1589,12 @@ public class ToOneAttributeMapping
return isConstrained;
}
+ public NotFoundAction getNotFoundAction() {
+ return notFoundAction;
+ }
+
public boolean isIgnoreNotFound(){
- return isIgnoreNotFound;
+ return notFoundAction == NotFoundAction.IGNORE;
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/package-info.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/package-info.java
index b1f1bfd825..55a00d5c23 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/package-info.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/package-info.java
@@ -8,4 +8,7 @@
/**
* Package defining a SQL AST for use in creating and executing various JDBC operations
*/
+@Incubating
package org.hibernate.sql.ast;
+
+import org.hibernate.Incubating;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java
index 1fd6f931ee..a6f62804d3 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java
@@ -7,6 +7,7 @@
package org.hibernate.sql.ast.tree.from;
import java.util.List;
+import java.util.Locale;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.NavigablePath;
@@ -37,8 +38,17 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
allowFkOptimization,
true
);
+
if ( tableReference == null ) {
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ tableExpression,
+ navigablePath.getFullPath()
+ )
+ );
}
return tableReference;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java
index a97cc8e325..19480dd454 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java
@@ -27,6 +27,8 @@ public interface ColumnReferenceQualifier {
* @param navigablePath The path for which to look up the table reference, may be null
* @param tableExpression The table expression for which to look up the table reference
* @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side
+ *
+ * @throws UnknownTableReferenceException to indicate that the given tableExpression could not be resolved
*/
TableReference resolveTableReference(
NavigablePath navigablePath,
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java
index c79f1d422e..1f17fe97ff 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java
@@ -47,7 +47,10 @@ public abstract class DerivedTableReference extends AbstractTableReference {
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ "TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath.getFullPath()
+ );
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java
index 0be805ec40..f47265e943 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java
@@ -9,6 +9,7 @@ package org.hibernate.sql.ast.tree.from;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -243,8 +244,17 @@ public class LazyTableGroup extends DelegatingTableGroup {
allowFkOptimization,
true
);
+
if ( tableReference == null ) {
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ tableExpression,
+ navigablePath.getFullPath()
+ )
+ );
}
return tableReference;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java
index aff550fb5c..d4cc3cf822 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java
@@ -8,6 +8,7 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -121,8 +122,17 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
allowFkOptimization,
true
);
+
if ( tableReference == null ) {
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ tableExpression,
+ navigablePath.getFullPath()
+ )
+ );
}
return tableReference;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java
index 86fade1644..4e34d94820 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java
@@ -8,6 +8,7 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -81,7 +82,16 @@ public class NamedTableReference extends AbstractTableReference {
if ( tableExpression.equals( getTableExpression() ) ) {
return this;
}
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ tableExpression,
+ navigablePath.getFullPath()
+ )
+ );
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java
index aa0d523f95..899ba70535 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java
@@ -6,7 +6,10 @@
*/
package org.hibernate.sql.ast.tree.from;
+import java.util.Locale;
+
import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.spi.NavigablePath;
/**
@@ -34,7 +37,16 @@ public class UnionTableReference extends NamedTableReference {
if ( hasTableExpression( tableExpression ) ) {
return this;
}
- throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
+
+ throw new UnknownTableReferenceException(
+ tableExpression,
+ String.format(
+ Locale.ROOT,
+ "Unable to determine TableReference (`%s`) for `%s`",
+ tableExpression,
+ navigablePath.getFullPath()
+ )
+ );
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnknownTableReferenceException.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnknownTableReferenceException.java
new file mode 100644
index 0000000000..0a1bad3b23
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnknownTableReferenceException.java
@@ -0,0 +1,30 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.sql.ast.tree.from;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Thrown when a {@link TableReference} cannot be resolved
+ * for a table-name.
+ *
+ * @see ColumnReferenceQualifier
+ *
+ * @author Steve Ebersole
+ */
+public class UnknownTableReferenceException extends HibernateException {
+ private final String tableExpression;
+
+ public UnknownTableReferenceException(String tableExpression, String message) {
+ super( message );
+ this.tableExpression = tableExpression;
+ }
+
+ public String getTableExpression() {
+ return tableExpression;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/package-info.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/package-info.java
index 77f0d5dcf9..88672cabda 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/exec/package-info.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/package-info.java
@@ -10,8 +10,11 @@
* to execute is modelled by {@link org.hibernate.sql.exec.spi.JdbcOperation} and
* are executed via the corresponding executor.
*
- * For operations that return ResultSets, be sure to see {@link org.hibernate.loader.ast.results}
+ * For operations that return ResultSets, be sure to see {@link org.hibernate.sql.results}
* which provides support for processing results starting with
* {@link org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping}
*/
+@Incubating
package org.hibernate.sql.exec;
+
+import org.hibernate.Incubating;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java
index 40b6d3046f..c7d197fee9 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java
@@ -231,6 +231,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
return navigablePath;
}
+ protected boolean isMissing() {
+ return missing;
+ }
+
protected abstract boolean isEntityReturn();
@Override
@@ -282,8 +286,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
return;
}
- if ( EntityLoadingLogger.TRACE_ENABLED ) {
- EntityLoadingLogger.LOGGER.tracef(
+ if ( EntityLoadingLogging.TRACE_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
"(%s) Beginning Initializer#resolveKey process for entity : %s",
StringHelper.collapse( this.getClass().getName() ),
getNavigablePath().getFullPath()
@@ -300,7 +304,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
resolveEntityKey( rowProcessingState );
if ( entityKey == null ) {
- EntityLoadingLogger.LOGGER.debugf(
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) EntityKey (%s) is null",
getSimpleConcreteImplName(),
getNavigablePath()
@@ -311,8 +315,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
return;
}
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Hydrated EntityKey (%s): %s",
getSimpleConcreteImplName(),
getNavigablePath(),
@@ -437,8 +441,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final Object entityIdentifier = entityKey.getIdentifier();
- if ( EntityLoadingLogger.TRACE_ENABLED ) {
- EntityLoadingLogger.LOGGER.tracef(
+ if ( EntityLoadingLogging.TRACE_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
"(%s) Beginning Initializer#resolveInstance process for entity (%s) : %s",
StringHelper.collapse( this.getClass().getName() ),
getNavigablePath(),
@@ -523,8 +527,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private void setIsOwningInitializer(Object entityIdentifier,LoadingEntityEntry existingLoadingEntry) {
if ( existingLoadingEntry != null ) {
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found existing loading entry [%s] - using loading instance",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier )
@@ -546,8 +550,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
SharedSessionContractImplementor session) {
if ( !isOwningInitializer ) {
// the entity is already being loaded elsewhere
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -598,8 +602,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
entityKey.getIdentifier()
);
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Created new entity instance [%s] : %s",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -680,8 +684,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final Object entityIdentifier = entityKey.getIdentifier();
- if ( EntityLoadingLogger.TRACE_ENABLED ) {
- EntityLoadingLogger.LOGGER.tracef(
+ if ( EntityLoadingLogging.TRACE_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
"(%s) Beginning Initializer#initializeInstance process for entity %s",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier )
@@ -778,8 +782,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// No need to put into the entity cache is this is coming from the query cache already
if ( !rowProcessingState.isQueryCacheHit() && cacheAccess != null && session.getCacheMode().isPutEnabled() ) {
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%S) Adding entityInstance to second-level cache: %s",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier )
@@ -871,8 +875,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
concreteDescriptor.afterInitialize( toInitialize, session );
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Done materializing entityInstance : %s",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier )
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogger.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogging.java
similarity index 54%
rename from hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogger.java
rename to hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogging.java
index 93e6c46c72..4fec363c29 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogger.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityLoadingLogging.java
@@ -13,14 +13,11 @@ import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
-public interface EntityLoadingLogger {
- String LOGGER_NAME = LoadingLogger.subLoggerName( "entity" );
+public interface EntityLoadingLogging {
+ String LOCAL_NAME = "entity";
+ String LOGGER_NAME = LoadingLogger.subLoggerName( LOCAL_NAME );
+ Logger ENTITY_LOADING_LOGGER = LoadingLogger.subLogger( LOCAL_NAME );
- /**
- * Static access to the logging instance
- */
- Logger LOGGER = LoadingLogger.subLogger( "entity" );
-
- boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
- boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
+ boolean TRACE_ENABLED = ENTITY_LOADING_LOGGER.isTraceEnabled();
+ boolean DEBUG_ENABLED = ENTITY_LOADING_LOGGER.isDebugEnabled();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java
index 17c5d55dec..2d816ca4c7 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java
@@ -28,7 +28,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
-import org.hibernate.sql.results.graph.entity.EntityLoadingLogger;
+import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@@ -109,8 +109,8 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
.findInitializer( entityKey );
if ( initializer != null ) {
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found an initializer for entity (%s) : %s",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -131,8 +131,8 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
if ( existingLoadingEntry != null ) {
if ( existingLoadingEntry.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java
index 402247ae74..9b6fa623be 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java
@@ -6,10 +6,16 @@
*/
package org.hibernate.sql.results.graph.entity.internal;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.FetchTiming;
+import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
+import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.spi.NavigablePath;
+import org.hibernate.sql.ast.spi.FromClauseAccess;
+import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.AssemblerCreationState;
+import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
@@ -22,25 +28,54 @@ import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
* @author Steve Ebersole
*/
public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch {
-
private final EntityResultImpl entityResult;
+ private final DomainResult> keyResult;
+ private final NotFoundAction notFoundAction;
+
private final String sourceAlias;
public EntityFetchJoinedImpl(
FetchParent fetchParent,
- EntityValuedFetchable fetchedAttribute,
+ ToOneAttributeMapping toOneMapping,
TableGroup tableGroup,
+ DomainResult> keyResult,
NavigablePath navigablePath,
DomainResultCreationState creationState) {
- super( fetchParent, fetchedAttribute, navigablePath );
+ super( fetchParent, toOneMapping, navigablePath );
+ this.keyResult = keyResult;
+ this.notFoundAction = toOneMapping.getNotFoundAction();
this.sourceAlias = tableGroup.getSourceAlias();
+
this.entityResult = new EntityResultImpl(
navigablePath,
- fetchedAttribute,
+ toOneMapping,
tableGroup,
null,
creationState
);
+
+ this.entityResult.afterInitialize( this, creationState );
+ }
+
+ public EntityFetchJoinedImpl(
+ FetchParent fetchParent,
+ EntityCollectionPart collectionPart,
+ TableGroup tableGroup,
+ NavigablePath navigablePath,
+ DomainResultCreationState creationState) {
+ super( fetchParent, collectionPart, navigablePath );
+ this.notFoundAction = null;
+ this.keyResult = null;
+ this.sourceAlias = tableGroup.getSourceAlias();
+
+ this.entityResult = new EntityResultImpl(
+ navigablePath,
+ collectionPart,
+ tableGroup,
+ null,
+ creationState
+ );
+
this.entityResult.afterInitialize( this, creationState );
}
@@ -56,6 +91,8 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch {
getReferencedModePart(),
getNavigablePath(),
creationState.determineEffectiveLockMode( sourceAlias ),
+ notFoundAction,
+ keyResult,
entityResult.getIdentifierFetch(),
entityResult.getDiscriminatorFetch(),
creationState
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchSelectImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchSelectImpl.java
index b1dd7fa6dc..6397e10b58 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchSelectImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchSelectImpl.java
@@ -24,18 +24,18 @@ import org.hibernate.sql.results.graph.entity.EntityInitializer;
* @author Andrea Boriero
*/
public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
- private final DomainResult result;
+ private final DomainResult keyResult;
private final boolean selectByUniqueKey;
public EntityFetchSelectImpl(
FetchParent fetchParent,
ToOneAttributeMapping fetchedAttribute,
NavigablePath navigablePath,
- DomainResult result,
+ DomainResult keyResult,
boolean selectByUniqueKey,
DomainResultCreationState creationState) {
super( navigablePath, fetchedAttribute, fetchParent );
- this.result = result;
+ this.keyResult = keyResult;
this.selectByUniqueKey = selectByUniqueKey;
}
@@ -65,7 +65,7 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
fetchedAttribute,
getNavigablePath(),
entityPersister,
- result.createResultAssembler( parentAccess, creationState )
+ keyResult.createResultAssembler( parentAccess, creationState )
);
}
if ( entityPersister.isBatchLoadable() ) {
@@ -74,7 +74,7 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
fetchedAttribute,
getNavigablePath(),
entityPersister,
- result.createResultAssembler( parentAccess, creationState )
+ keyResult.createResultAssembler( parentAccess, creationState )
);
}
else {
@@ -83,7 +83,7 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
fetchedAttribute,
getNavigablePath(),
entityPersister,
- result.createResultAssembler( parentAccess, creationState )
+ keyResult.createResultAssembler( parentAccess, creationState )
);
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java
index baf694c528..35fb485c20 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java
@@ -6,17 +6,23 @@
*/
package org.hibernate.sql.results.graph.entity.internal;
+import org.hibernate.FetchNotFoundException;
import org.hibernate.LockMode;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState;
+import org.hibernate.sql.results.graph.DomainResult;
+import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
+import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
+import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
* @author Andrea Boriero
@@ -24,13 +30,18 @@ import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
private static final String CONCRETE_NAME = EntityJoinedFetchInitializer.class.getSimpleName();
+ private final DomainResultAssembler> keyAssembler;
+ private final EntityValuedFetchable referencedFetchable;
private final boolean isEnhancedForLazyLoading;
+ private final NotFoundAction notFoundAction;
public EntityJoinedFetchInitializer(
EntityResultGraphNode resultDescriptor,
EntityValuedFetchable referencedFetchable,
NavigablePath navigablePath,
LockMode lockMode,
+ NotFoundAction notFoundAction,
+ DomainResult> keyResult,
Fetch identifierFetch,
Fetch discriminatorFetch,
AssemblerCreationState creationState) {
@@ -43,8 +54,14 @@ public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
null,
creationState
);
+ this.referencedFetchable = referencedFetchable;
+ this.notFoundAction = notFoundAction;
+
+ this.keyAssembler = keyResult == null ? null : keyResult.createResultAssembler( this, creationState );
+
if ( getConcreteDescriptor() != null ) {
- this.isEnhancedForLazyLoading = getConcreteDescriptor().getBytecodeEnhancementMetadata()
+ this.isEnhancedForLazyLoading = getConcreteDescriptor()
+ .getBytecodeEnhancementMetadata()
.isEnhancedForLazyLoading();
}
else {
@@ -52,6 +69,37 @@ public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
}
}
+ @Override
+ public void resolveKey(RowProcessingState rowProcessingState) {
+ super.resolveKey( rowProcessingState );
+
+ // super processes the foreign-key target column. here we
+ // need to also look at the foreign-key value column to check
+ // for a dangling foreign-key
+
+ if ( notFoundAction != null && keyAssembler != null ) {
+ final Object fkKeyValue = keyAssembler.assemble( rowProcessingState );
+ if ( fkKeyValue != null ) {
+ if ( isMissing() ) {
+ if ( notFoundAction == NotFoundAction.EXCEPTION ) {
+ throw new FetchNotFoundException(
+ referencedFetchable.getEntityMappingType().getEntityName(),
+ fkKeyValue
+ );
+ }
+ else {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
+ "Ignoring dangling foreign-key due to `@NotFound(IGNORE); association will be null - %s",
+ getNavigablePath()
+ );
+ }
+ }
+ }
+ }
+ }
+
+
+
@Override
protected Object getProxy(PersistenceContext persistenceContext) {
ModelPart referencedModelPart = getInitializedPart();
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java
index 6097be72ae..1d4e2c5e61 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java
@@ -30,8 +30,8 @@ public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchIn
ToOneAttributeMapping fetchedAttribute,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
- DomainResultAssembler identifierAssembler) {
- super( parentAccess, fetchedAttribute, fetchedNavigable, concreteDescriptor, identifierAssembler );
+ DomainResultAssembler> keyAssembler) {
+ super( parentAccess, fetchedAttribute, fetchedNavigable, concreteDescriptor, keyAssembler );
this.fetchedAttribute = fetchedAttribute;
}
@@ -46,7 +46,7 @@ public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchIn
return;
}
- final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
+ final Object entityIdentifier = keyAssembler.assemble( rowProcessingState );
if ( entityIdentifier == null ) {
isInitialized = true;
return;
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java
index af25f46e0c..7dcd879621 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java
@@ -8,6 +8,8 @@ package org.hibernate.sql.results.graph.entity.internal;
import java.util.function.Consumer;
+import org.hibernate.FetchNotFoundException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@@ -19,14 +21,14 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
-import org.hibernate.query.sqm.spi.EntityIdentifierNavigablePath;
import org.hibernate.query.spi.NavigablePath;
+import org.hibernate.query.sqm.spi.EntityIdentifierNavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
-import org.hibernate.sql.results.graph.entity.EntityLoadingLogger;
+import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@@ -43,28 +45,30 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
private final boolean isEnhancedForLazyLoading;
protected final EntityPersister concreteDescriptor;
- protected final DomainResultAssembler identifierAssembler;
- private final ToOneAttributeMapping referencedModelPart;
+ protected final DomainResultAssembler> keyAssembler;
+ private final ToOneAttributeMapping toOneMapping;
+
+ private Object entityIdentifier;
protected boolean isInitialized;
protected Object entityInstance;
public EntitySelectFetchInitializer(
FetchParentAccess parentAccess,
- ToOneAttributeMapping referencedModelPart,
+ ToOneAttributeMapping toOneMapping,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
- DomainResultAssembler identifierAssembler) {
+ DomainResultAssembler> keyAssembler) {
this.parentAccess = parentAccess;
- this.referencedModelPart = referencedModelPart;
+ this.toOneMapping = toOneMapping;
this.navigablePath = fetchedNavigable;
this.concreteDescriptor = concreteDescriptor;
- this.identifierAssembler = identifierAssembler;
+ this.keyAssembler = keyAssembler;
this.isEnhancedForLazyLoading = concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
}
public ModelPart getInitializedPart(){
- return referencedModelPart;
+ return toOneMapping;
}
@Override
@@ -101,15 +105,15 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
return;
}
- final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
+ final Object entityIdentifier = keyAssembler.assemble( rowProcessingState );
if ( entityIdentifier == null ) {
isInitialized = true;
return;
}
- if ( EntityLoadingLogger.TRACE_ENABLED ) {
- EntityLoadingLogger.LOGGER.tracef(
+ if ( EntityLoadingLogging.TRACE_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
"(%s) Beginning Initializer#resolveInstance process for entity (%s) : %s",
StringHelper.collapse( this.getClass().getName() ),
getNavigablePath(),
@@ -132,8 +136,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
.findInitializer( entityKey );
if ( initializer != null ) {
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found an initializer for entity (%s) : %s",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -152,8 +156,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
.findLoadingEntityEntry( entityKey );
if ( existingLoadingEntry != null ) {
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found existing loading entry [%s] - using loading instance",
CONCRETE_NAME,
toLoggableString(
@@ -166,8 +170,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
if ( existingLoadingEntry.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -181,8 +185,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
}
}
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Invoking session#internalLoad for entity (%s) : %s",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -193,11 +197,17 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
entityName,
entityIdentifier,
true,
- referencedModelPart.isNullable() || referencedModelPart.isIgnoreNotFound()
+ toOneMapping.isNullable() || toOneMapping.isIgnoreNotFound()
);
- if ( EntityLoadingLogger.DEBUG_ENABLED ) {
- EntityLoadingLogger.LOGGER.debugf(
+ if ( entityInstance == null ) {
+ if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) {
+ throw new FetchNotFoundException( entityName, entityIdentifier );
+ }
+ }
+
+ if ( EntityLoadingLogging.DEBUG_ENABLED ) {
+ EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] : %s has being loaded by session.internalLoad.",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
@@ -205,7 +215,7 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
);
}
- final boolean unwrapProxy = referencedModelPart.isUnwrapProxy() && isEnhancedForLazyLoading;
+ final boolean unwrapProxy = toOneMapping.isUnwrapProxy() && isEnhancedForLazyLoading;
if ( entityInstance instanceof HibernateProxy ) {
( (HibernateProxy) entityInstance ).getHibernateLazyInitializer().setUnwrap( unwrapProxy );
}
@@ -216,7 +226,7 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
if ( parentAccess instanceof EntityInitializer ) {
final AbstractEntityPersister concreteDescriptor = (AbstractEntityPersister) ( (EntityInitializer) parentAccess ).getConcreteDescriptor();
if ( concreteDescriptor.isPolymorphic() ) {
- final AbstractEntityPersister declaringType = (AbstractEntityPersister) referencedModelPart.getDeclaringType();
+ final AbstractEntityPersister declaringType = (AbstractEntityPersister) toOneMapping.getDeclaringType();
if ( concreteDescriptor != declaringType ) {
if ( !declaringType.getSubclassEntityNames().contains( concreteDescriptor.getName() ) ) {
return false;
@@ -229,6 +239,7 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
+ entityIdentifier = null;
entityInstance = null;
isInitialized = false;
clearResolutionListeners();
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java
index 6a1a85f70f..d208ad5338 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java
@@ -106,10 +106,10 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
}
@Override
- public DomainResultAssembler createAssembler(
+ public DomainResultAssembler> createAssembler(
FetchParentAccess parentAccess,
AssemblerCreationState creationState) {
- final DomainResultAssembler resultAssembler = keyResult.createResultAssembler( parentAccess, creationState );
+ final DomainResultAssembler> keyAssembler = keyResult.createResultAssembler( parentAccess, creationState );
final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer(
getNavigablePath(),
@@ -122,7 +122,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
fetchable,
getNavigablePath(),
entityMappingType.getEntityPersister(),
- resultAssembler
+ keyAssembler
);
}
final EntityPersister entityPersister = entityMappingType.getEntityPersister();
@@ -132,7 +132,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
- resultAssembler
+ keyAssembler
);
}
else {
@@ -141,7 +141,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
- resultAssembler
+ keyAssembler
);
}
}
@@ -151,7 +151,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
getReferencedPath(),
fetchable,
selectByUniqueKey,
- resultAssembler
+ keyAssembler
);
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/package-info.java b/hibernate-core/src/main/java/org/hibernate/sql/results/package-info.java
index 8a50b8727d..218099598d 100644
--- a/hibernate-core/src/main/java/org/hibernate/sql/results/package-info.java
+++ b/hibernate-core/src/main/java/org/hibernate/sql/results/package-info.java
@@ -10,4 +10,7 @@
* defined by a "domain result graph" - one or more {@link org.hibernate.sql.results.graph.DomainResult} nodes
* with zero-or-more {@link org.hibernate.sql.results.graph.Fetch} nodes
*/
+@Incubating
package org.hibernate.sql.results;
+
+import org.hibernate.Incubating;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingTest.java
index a43b26a882..d408d7729c 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
import org.jboss.logging.Logger;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -80,8 +81,9 @@ public class JoinFormulaManyToOneNotIgnoreLazyFetchingTest extends BaseEntityMan
@Test
public void testLazyLoading() {
-
- assertEquals( "HHH000491: The [code] association in the [" + Stock.class.getName() + "] entity uses both @NotFound(action = NotFoundAction.IGNORE) and FetchType.LAZY. The NotFoundAction.IGNORE @ManyToOne and @OneToOne associations are always fetched eagerly.", triggerable.triggerMessage() );
+ assertThat( triggerable.wasTriggered() )
+ .describedAs( "Expecting WARN message to be logged" )
+ .isTrue();
List stocks = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.createQuery(
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToManyNotIgnoreLazyFetchingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToManyNotIgnoreLazyFetchingTest.java
index 07a391ffaa..1403281f29 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToManyNotIgnoreLazyFetchingTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToManyNotIgnoreLazyFetchingTest.java
@@ -23,18 +23,22 @@ import org.hibernate.LazyInitializationException;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.cfg.AnnotationBinder;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
+import org.hibernate.testing.transaction.TransactionUtil2;
import org.junit.Rule;
import org.junit.Test;
import org.jboss.logging.Logger;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
+import static org.hibernate.testing.transaction.TransactionUtil2.fromTransaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -84,21 +88,17 @@ public class JoinFormulaOneToManyNotIgnoreLazyFetchingTest extends BaseEntityMan
assertFalse( triggerable.wasTriggered() );
- List stocks = doInJPA( this::entityManagerFactory, entityManager -> {
- return entityManager.createQuery(
- "SELECT s FROM Stock s", Stock.class )
- .getResultList();
+ List stocks = fromTransaction( entityManagerFactory().unwrap( SessionFactoryImplementor.class ), (session) -> {
+ return session.createQuery("SELECT s FROM Stock s order by id", Stock.class ).getResultList();
} );
- assertEquals( 2, stocks.size() );
- try {
- assertEquals( "ABC", stocks.get( 0 ).getCodes().get( 0 ).getRefNumber() );
+ assertThat( stocks ).hasSize( 2 );
- fail( "Should have thrown LazyInitializationException" );
- }
- catch (LazyInitializationException expected) {
+ final Stock firstStock = stocks.get( 0 );
+ final Stock secondStock = stocks.get( 1 );
- }
+ assertThat( firstStock.getCodes() ).hasSize( 1 );
+ assertThat( secondStock.getCodes() ).hasSize( 0 );
}
@Entity(name = "Stock")
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingTest.java
index 90ce73feec..25ad48f306 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingTest.java
@@ -34,6 +34,7 @@ import org.junit.Test;
import org.jboss.logging.Logger;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -79,8 +80,9 @@ public class JoinFormulaOneToOneNotIgnoreLazyFetchingTest extends BaseEntityMana
@Test
public void testLazyLoading() {
-
- assertEquals( "HHH000491: The [code] association in the [" + Stock.class.getName() + "] entity uses both @NotFound(action = NotFoundAction.IGNORE) and FetchType.LAZY. The NotFoundAction.IGNORE @ManyToOne and @OneToOne associations are always fetched eagerly.", triggerable.triggerMessage() );
+ assertThat( triggerable.wasTriggered() )
+ .describedAs( "Expecting WARN message to be logged" )
+ .isTrue();
List stocks = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.createQuery(
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundManyToOneNonUpdatableNonInsertableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundManyToOneNonUpdatableNonInsertableTest.java
index 6e398e9898..3369b77f5d 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundManyToOneNonUpdatableNonInsertableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundManyToOneNonUpdatableNonInsertableTest.java
@@ -28,6 +28,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -70,7 +71,10 @@ public class LazyNotFoundManyToOneNonUpdatableNonInsertableTest extends BaseCore
doInHibernate(
this::sessionFactory, session -> {
User user = session.find( User.class, ID );
- assertFalse( Hibernate.isPropertyInitialized( user, "lazy" ) );
+ // per UserGuide (and simply correct behavior), `@NotFound` forces EAGER fetching
+ assertThat( Hibernate.isPropertyInitialized( user, "lazy" ) )
+ .describedAs( "`User#lazy` is not eagerly initialized due to presence of `@NotFound`" )
+ .isTrue();
assertNull( user.getLazy() );
}
);
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneNonUpdatableNonInsertableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneNonUpdatableNonInsertableTest.java
index 6d141aad2a..0fe3bfd928 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneNonUpdatableNonInsertableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneNonUpdatableNonInsertableTest.java
@@ -32,6 +32,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -72,9 +73,12 @@ public class LazyNotFoundOneToOneNonUpdatableNonInsertableTest extends BaseCoreF
doInHibernate(
this::sessionFactory, session -> {
User user = session.find( User.class, ID );
- assertFalse( Hibernate.isPropertyInitialized( user, "lazy" ) );
- assertNull( user.getLazy() );
- assertTrue( Hibernate.isPropertyInitialized( user, "lazy" ) );
+ assertThat( Hibernate.isPropertyInitialized( user, "lazy" ) )
+ .describedAs( "Expecting `User#lazy` to be bytecode initialized due to `@NotFound`" )
+ .isTrue();
+ assertThat( user.getLazy() )
+ .describedAs( "Expecting `User#lazy` to be null due to `NotFoundAction#IGNORE`" )
+ .isNull();
}
);
}
@@ -90,7 +94,6 @@ public class LazyNotFoundOneToOneNonUpdatableNonInsertableTest extends BaseCoreF
@LazyToOne(value = LazyToOneOption.NO_PROXY)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(
- foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT),
name = "id",
referencedColumnName = "id",
insertable = false,
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneTest.java
index e5c737c5b1..7fae20fcc9 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/notfound/LazyNotFoundOneToOneTest.java
@@ -30,11 +30,11 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
/**
* @author Gail Badner
@@ -85,8 +85,16 @@ public class LazyNotFoundOneToOneTest extends BaseCoreFunctionalTestCase {
this::sessionFactory, session -> {
User user = session.find( User.class, ID );
- assertThat( sqlInterceptor.getQueryCount(), is( 1 ) );
- assertFalse( Hibernate.isPropertyInitialized( user, "lazy" ) );
+ // per UserGuide (and simply correct behavior), `@NotFound` forces EAGER fetching
+ assertThat( sqlInterceptor.getQueryCount() ).
+ describedAs( "Expecting 2 queries due to `@NotFound`" )
+ .isEqualTo( 2 );
+ assertThat( Hibernate.isPropertyInitialized( user, "lazy" ) )
+ .describedAs( "Expecting `User#lazy` to be eagerly fetched due to `@NotFound`" )
+ .isTrue();
+ assertThat( Hibernate.isInitialized( user.getLazy() ) )
+ .describedAs( "Expecting `User#lazy` to be eagerly fetched due to `@NotFound`" )
+ .isTrue();
assertNull( user.getLazy() );
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/annotations/CollectionBinderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/annotations/CollectionBinderTest.java
index d2546e4fc3..b06781322f 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/annotations/CollectionBinderTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/annotations/CollectionBinderTest.java
@@ -11,6 +11,7 @@ import java.util.HashMap;
import java.util.Map;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
@@ -78,7 +79,7 @@ public class CollectionBinderTest extends BaseUnitTestCase {
AnnotatedJoinColumn[] fkJoinColumns,
XClass collectionType,
boolean cascadeDeleteEnabled,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext,
Map inheritanceStatePerClass) {
super.bindOneToManySecondPass(
@@ -87,12 +88,12 @@ public class CollectionBinderTest extends BaseUnitTestCase {
fkJoinColumns,
collectionType,
cascadeDeleteEnabled,
- ignoreNotFound,
+ notFoundAction,
buildingContext,
inheritanceStatePerClass
);
}
- }.bindOneToManySecondPass( collection, new HashMap(), null, collectionType, false, false, buildingContext, null);
+ }.bindOneToManySecondPass( collection, new HashMap(), null, collectionType, false, null, buildingContext, null);
} catch (MappingException e) {
assertEquals(expectMessage, e.getMessage());
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionLogicalOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionLogicalOneToOneTest.java
index 68bcb38295..3051bf15a2 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionLogicalOneToOneTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionLogicalOneToOneTest.java
@@ -9,10 +9,12 @@ package org.hibernate.orm.test.notfound.exception;
import java.io.Serializable;
import java.util.List;
import jakarta.persistence.Entity;
+import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
+import org.hibernate.FetchNotFoundException;
import org.hibernate.Hibernate;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.annotations.NotFound;
@@ -65,7 +67,6 @@ public class NotFoundExceptionLogicalOneToOneTest {
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected( reason = "We return a proxy here for `Coin#currency`, which violates NOTE #1." )
public void testGet(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@@ -73,18 +74,24 @@ public class NotFoundExceptionLogicalOneToOneTest {
scope.inTransaction( (session) -> {
session.get( Coin.class, 2 );
- // here we assume join, but this could use subsequent-select instead
- assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
+ // at the moment this is handled as SELECT fetch
+ assertThat( statementInspector.getSqlQueries() ).hasSize( 2 );
+
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
+
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).contains( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
} );
scope.inTransaction( (session) -> {
try {
- session.get( Coin.class, 1 );
- fail( "Expecting ObjectNotFoundException" );
+ final Coin coin = session.get( Coin.class, 1 );
+ fail( "Expecting ObjectNotFoundException, got - coin = " + coin + "; currency = " + coin.currency );
}
- catch (ObjectNotFoundException expected) {
+ catch (FetchNotFoundException expected) {
assertThat( expected.getEntityName() ).isEqualTo( Currency.class.getName() );
assertThat( expected.getIdentifier() ).isEqualTo( 1 );
}
@@ -93,7 +100,7 @@ public class NotFoundExceptionLogicalOneToOneTest {
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected( reason = "We return a proxy for `Coin#currency`, which violates NOTE #1." )
+ @FailureExpected( reason = "Join is not used in the SQL" )
public void testQueryImplicitPathDereferencePredicate(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@@ -123,16 +130,15 @@ public class NotFoundExceptionLogicalOneToOneTest {
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected( reason = "We return a proxy for `Coin#currency`, which violates NOTE #1." )
public void testQueryOwnerSelection(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final String hql = "select c from Coin c where c.id = 1";
try {
//noinspection unused (debugging)
final Coin coin = session.createQuery( hql, Coin.class ).uniqueResult();
- fail( "Expecting ObjectNotFoundException for broken fk" );
+ fail( "Expecting ObjectNotFoundException; got - currency = " + coin.currency + "; coin = " + coin );
}
- catch (ObjectNotFoundException expected) {
+ catch (FetchNotFoundException expected) {
assertThat( expected.getEntityName() ).isEqualTo( Currency.class.getName() );
assertThat( expected.getIdentifier() ).isEqualTo( 1 );
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionManyToOneTest.java
index 3c9b767891..0049d710f7 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionManyToOneTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/exception/NotFoundExceptionManyToOneTest.java
@@ -13,6 +13,7 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
+import org.hibernate.FetchNotFoundException;
import org.hibernate.Hibernate;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.annotations.NotFound;
@@ -66,9 +67,6 @@ public class NotFoundExceptionManyToOneTest {
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected(
- reason = "ObjectNotFoundException is thrown, but caught in `IdentifierLoadAccessImpl#doLoad` and null returned instead"
- )
public void testGet(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@@ -76,10 +74,10 @@ public class NotFoundExceptionManyToOneTest {
scope.inTransaction( (session) -> {
try {
// should fail here loading the Coin due to missing currency (see NOTE#1)
- session.get( Coin.class, 1 );
- fail( "Expecting ObjectNotFoundException for broken fk" );
+ final Coin coin = session.get( Coin.class, 1 );
+ fail( "Expecting ObjectNotFoundException - " + coin.getCurrency() );
}
- catch (ObjectNotFoundException expected) {
+ catch (FetchNotFoundException expected) {
// technically we could use a subsequent-select rather than a join...
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
@@ -122,11 +120,6 @@ public class NotFoundExceptionManyToOneTest {
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected(
- reason = "Does not do the join. Instead selects the Coin based on `currency_id` and then " +
- "subsequent-selects the Currency. Ultimately results in a `Coin#1` reference with a " +
- "null Currency."
- )
public void testQueryOwnerSelection(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@@ -137,37 +130,23 @@ public class NotFoundExceptionManyToOneTest {
session.createQuery( hql, Coin.class ).getResultList();
fail( "Expecting ObjectNotFoundException for broken fk" );
}
- catch (ObjectNotFoundException expected) {
+ catch (FetchNotFoundException expected) {
assertThat( expected.getEntityName() ).isEqualTo( Currency.class.getName() );
assertThat( expected.getIdentifier() ).isEqualTo( 1 );
-
- // technically we could use a subsequent-select rather than a join...
- assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
}
} );
}
@Test
@JiraKey( "HHH-15060" )
- @FailureExpected(
- reason = "This one is somewhat debatable. Is this selecting the association? Or simply matching Currencies?"
- )
public void testQueryAssociationSelection(SessionFactoryScope scope) {
// NOTE: this one is not obvious
// - we are selecting the association so from that perspective, throwing the ObjectNotFoundException is nice
// - the other way to look at it is that there are simply no matching results, so nothing to return
scope.inTransaction( (session) -> {
final String hql = "select c.currency from Coin c where c.id = 1";
- try {
- session.createQuery( hql, Currency.class ).getResultList();
- fail( "Expecting ObjectNotFoundException for broken fk" );
- }
- catch (ObjectNotFoundException expected) {
- assertThat( expected.getEntityName() ).isEqualTo( Currency.class.getName() );
- assertThat( expected.getIdentifier() ).isEqualTo( 1 );
- }
+ final List resultList = session.createQuery( hql, Currency.class ).getResultList();
+ assertThat( resultList ).isEmpty();
} );
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreManyToOneTest.java
index a5977e3f90..caf0b53693 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreManyToOneTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreManyToOneTest.java
@@ -114,13 +114,14 @@ public class NotFoundIgnoreManyToOneTest {
// at the moment this uses a subsequent-select. on the bright side, it is at least eagerly fetched.
assertThat( statementInspector.getSqlQueries() ).hasSize( 2 );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " from Coin " );
- assertThat( statementInspector.getSqlQueries().get( 1 ) ).contains( " from Currency " );
- // but I believe a jon would be better
-// assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
-// assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
-// assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
+
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).contains( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
} );
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreOneToOneTest.java
index 84d3f3ef0b..feb12a4f1b 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreOneToOneTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/notfound/ignore/NotFoundIgnoreOneToOneTest.java
@@ -116,13 +116,14 @@ public class NotFoundIgnoreOneToOneTest {
// at the moment this uses a subsequent-select. on the bright side, it is at least eagerly fetched.
assertThat( statementInspector.getSqlQueries() ).hasSize( 2 );
- assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " from Coin " );
- assertThat( statementInspector.getSqlQueries().get( 1 ) ).contains( " from Currency " );
- // but I believe a jon would be better
-// assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
-// assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
-// assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
+
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).contains( " Currency " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " Coin " );
+ assertThat( statementInspector.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
} );
}
diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java
index 9f52b474e1..6739b84901 100644
--- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java
+++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java
@@ -155,13 +155,13 @@ public class SessionFactoryExtension
}
final HashMap settings = new HashMap<>( baseProperties );
- settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP );
+ settings.put( AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP );
if ( createSecondarySchemas ) {
if ( !( model.getDatabase().getDialect().canCreateSchema() ) ) {
throw new UnsupportedOperationException(
model.getDatabase().getDialect() + " does not support schema creation" );
}
- settings.put( AvailableSettings.HBM2DDL_CREATE_SCHEMAS, true );
+ settings.put( AvailableSettings.JAKARTA_HBM2DDL_CREATE_SCHEMAS, true );
}
final StandardServiceRegistry serviceRegistry = model.getMetadataBuildingOptions().getServiceRegistry();
@@ -169,7 +169,7 @@ public class SessionFactoryExtension
model,
serviceRegistry,
settings,
- action -> sessionFactory.addObserver(
+ (action) -> sessionFactory.addObserver(
new SessionFactoryObserver() {
@Override
public void sessionFactoryClosing(org.hibernate.SessionFactory factory) {