HHH-15099 - Improve handling of associations marked with @NotFound
- Keep track of NotFoundAction into mapping model - Fix tests with erroneous assertions about `@NotFound` associations allowed to be lazy
This commit is contained in:
parent
de97e8e1a4
commit
d52ebfb41d
|
@ -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.
|
||||
====
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -7,27 +7,38 @@
|
|||
package org.hibernate.annotations;
|
||||
|
||||
/**
|
||||
* Enumerates the association fetching strategies available in Hibernate.
|
||||
* <p>
|
||||
* Whereas the JPA {@link jakarta.persistence.FetchType} enumeration provides a way to
|
||||
* specify <em>when</em> an association should be fetched, this enumeration provides a
|
||||
* way to express <em>how</em> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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,9 +987,30 @@ 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 ( notFound != null ) {
|
||||
collection.setLazy( false );
|
||||
|
||||
if ( lazy != null ) {
|
||||
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
|
||||
}
|
||||
|
||||
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 {
|
||||
collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( lazy != null ) {
|
||||
collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
|
||||
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
|
||||
|
@ -1017,6 +1040,7 @@ public abstract class CollectionBinder {
|
|||
collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private XClass getCollectionType() {
|
||||
if ( AnnotationBinder.isDefault( targetEntity, buildingContext ) ) {
|
||||
|
@ -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<XClass, InheritanceState> 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) {
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -277,6 +277,7 @@ public class BasicAttributeMapping
|
|||
getContainingTableExpression(),
|
||||
allowFkOptimization
|
||||
);
|
||||
|
||||
return expressionResolver.resolveSqlSelection(
|
||||
expressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
|
|
|
@ -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<String> 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<String> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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(
|
||||
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(
|
||||
|
|
|
@ -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,7 +939,6 @@ public class ToOneAttributeMapping
|
|||
}
|
||||
// The referencedNavigablePath can be null if this is a collection initialization
|
||||
if ( referencedNavigablePath != 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;
|
||||
|
@ -945,6 +955,8 @@ public class ToOneAttributeMapping
|
|||
else {
|
||||
keyDomainResult = null;
|
||||
}
|
||||
|
||||
if ( hasBidirectionalFetchParent ) {
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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 ),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Stock> stocks = doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery(
|
||||
|
|
|
@ -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<Stock> stocks = doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery(
|
||||
"SELECT s FROM Stock s", Stock.class )
|
||||
.getResultList();
|
||||
List<Stock> 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")
|
||||
|
|
|
@ -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<Stock> stocks = doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery(
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
|
|
|
@ -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<XClass, InheritanceState> 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());
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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<Currency> resultList = session.createQuery( hql, Currency.class ).getResultList();
|
||||
assertThat( resultList ).isEmpty();
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -155,13 +155,13 @@ public class SessionFactoryExtension
|
|||
}
|
||||
|
||||
final HashMap<String,Object> 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) {
|
||||
|
|
Loading…
Reference in New Issue