Work on named-native query support

This commit is contained in:
Christian Beikov 2021-03-23 16:07:26 +01:00
parent ae69a1aeb4
commit c4445fbf5c
43 changed files with 1209 additions and 529 deletions

View File

@ -67,6 +67,7 @@ public class ColumnTransformerTest extends BaseEntityManagerFunctionalTestCase {
read = "money / 100", read = "money / 100",
write = "? * 100" write = "? * 100"
) )
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
private MonetaryAmount wallet; private MonetaryAmount wallet;
//Getters and setters omitted for brevity //Getters and setters omitted for brevity

View File

@ -52,8 +52,8 @@ import javax.persistence.SqlResultSetMapping;
@FieldResult(name = "name", column = "name"), @FieldResult(name = "name", column = "name"),
@FieldResult(name = "model", column = "model"), @FieldResult(name = "model", column = "model"),
@FieldResult(name = "speed", column = "speed"), @FieldResult(name = "speed", column = "speed"),
@FieldResult(name = "captain.lastname", column = "lastn"), @FieldResult(name = "captain.id.lastname", column = "lastn"),
@FieldResult(name = "captain.firstname", column = "firstn"), @FieldResult(name = "captain.id.firstname", column = "firstn"),
@FieldResult(name = "dimensions.length", column = "length"), @FieldResult(name = "dimensions.length", column = "length"),
@FieldResult(name = "dimensions.width", column = "width"), @FieldResult(name = "dimensions.width", column = "width"),
} }

View File

@ -15,6 +15,8 @@ import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode; import javax.persistence.ParameterMode;
import javax.persistence.StoredProcedureParameter; import javax.persistence.StoredProcedureParameter;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.query.NamedProcedureCallDefinition; import org.hibernate.boot.query.NamedProcedureCallDefinition;
@ -23,8 +25,12 @@ import org.hibernate.cfg.annotations.QueryHintDefinition;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.procedure.internal.NamedCallableQueryMementoImpl;
import org.hibernate.procedure.internal.Util;
import org.hibernate.procedure.spi.NamedCallableQueryMemento; import org.hibernate.procedure.spi.NamedCallableQueryMemento;
import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.query.internal.ResultSetMappingResolutionContext;
import org.hibernate.query.results.ResultSetMappingImpl;
import static org.hibernate.procedure.spi.NamedCallableQueryMemento.ParameterMemento; import static org.hibernate.procedure.spi.NamedCallableQueryMemento.ParameterMemento;
@ -44,7 +50,7 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
private final ParameterDefinitions parameterDefinitions; private final ParameterDefinitions parameterDefinitions;
private final Map<String, Object> hints; private final Map<String, Object> hints;
NamedProcedureCallDefinitionImpl(NamedStoredProcedureQuery annotation) { public NamedProcedureCallDefinitionImpl(NamedStoredProcedureQuery annotation) {
this.registeredName = annotation.name(); this.registeredName = annotation.name();
this.procedureName = annotation.procedureName(); this.procedureName = annotation.procedureName();
this.hints = new QueryHintDefinition( registeredName, annotation.hints() ).getHintsMap(); this.hints = new QueryHintDefinition( registeredName, annotation.hints() ).getHintsMap();
@ -84,12 +90,45 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0; final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0; final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
// if ( specifiesResultClasses ) { ResultSetMappingImpl resultSetMapping = new ResultSetMappingImpl( registeredName );
// Util.resolveResultClasses(
// new Util.ResultClassesResolutionContext() { if ( specifiesResultClasses ) {
Util.resolveResultSetMappingClasses(
resultClasses,
resultSetMapping,
collectedQuerySpaces::add,
new ResultSetMappingResolutionContext() {
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
// @Override // @Override
// public SessionFactoryImplementor getSessionFactory() { // public void addQueryReturns(NativeSQLQueryReturn... queryReturns) {
// return sessionFactory; // Collections.addAll( collectedQueryReturns, queryReturns );
// }
//
// @Override
// public void addQuerySpaces(String... spaces) {
// Collections.addAll( collectedQuerySpaces, spaces );
// }
}
);
}
else if ( specifiesResultSetMappings ) {
Util.resolveResultSetMappingNames(
resultSetMappings,
resultSetMapping,
collectedQuerySpaces::add,
new ResultSetMappingResolutionContext() {
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
// @Override
// public NamedResultSetMappingMemento findResultSetMapping(String name) {
// return sessionFactory.getQueryEngine().getNamedObjectRepository().getResultSetMappingMemento( name );
// } // }
// //
// @Override // @Override
@ -101,47 +140,28 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
// public void addQuerySpaces(String... spaces) { // public void addQuerySpaces(String... spaces) {
// Collections.addAll( collectedQuerySpaces, spaces ); // Collections.addAll( collectedQuerySpaces, spaces );
// } // }
// }, }
// resultClasses );
// ); }
// }
// else if ( specifiesResultSetMappings ) { return new NamedCallableQueryMementoImpl(
// Util.resolveResultSetMappings( getRegistrationName(),
// new Util.ResultSetMappingResolutionContext() { procedureName,
// @Override parameterDefinitions.getParameterStrategy(),
// public SessionFactoryImplementor getSessionFactory() { parameterDefinitions.toMementos( sessionFactory ),
// return sessionFactory; resultSetMappings,
// } resultClasses,
// collectedQuerySpaces,
// @Override false,
// public ResultSetMappingDescriptor findResultSetMapping(String name) { null,
// return sessionFactory.getQueryEngine().getNamedQueryRepository().getResultSetMappingMemento( name ); CacheMode.IGNORE,
// } FlushMode.AUTO,
// false,
// @Override null,
// public void addQueryReturns(NativeSQLQueryReturn... queryReturns) { null,
// Collections.addAll( collectedQueryReturns, queryReturns ); null,
// } hints
// );
// @Override
// public void addQuerySpaces(String... spaces) {
// Collections.addAll( collectedQuerySpaces, spaces );
// }
// },
// resultSetMappings
// );
// }
//
// return new NamedCallableQueryMementoImpl(
// getRegistrationName(),
// procedureName,
// collectedQueryReturns.toArray( new NativeSQLQueryReturn[ collectedQueryReturns.size() ] ),
// parameterDefinitions.getParameterStrategy(),
// parameterDefinitions.toMementos( sessionFactory ),
// collectedQuerySpaces,
// hints
// );
throw new NotYetImplementedFor6Exception( getClass() );
} }
static class ParameterDefinitions { static class ParameterDefinitions {
@ -234,19 +254,19 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
@SuppressWarnings("UnnecessaryUnboxing") @SuppressWarnings("UnnecessaryUnboxing")
public ParameterMemento toMemento(SessionFactoryImplementor sessionFactory) { public ParameterMemento toMemento(SessionFactoryImplementor sessionFactory) {
final boolean initialPassNullSetting = explicitPassNullSetting != null // todo (6.0): figure out how to handle this
? explicitPassNullSetting.booleanValue() // final boolean initialPassNullSetting = explicitPassNullSetting != null
: sessionFactory.getSessionFactoryOptions().isProcedureParameterNullPassingEnabled(); // ? explicitPassNullSetting.booleanValue()
// : sessionFactory.getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
// return new ParameterMemento( return new NamedCallableQueryMementoImpl.ParameterMementoImpl(
// position, position,
// name, name,
// parameterMode, parameterMode,
// type, type,
// sessionFactory.getTypeResolver().heuristicType( type.getName() ), sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( type )
// initialPassNullSetting // ,initialPassNullSetting
// ); );
throw new NotYetImplementedFor6Exception( getClass() );
} }
} }

View File

@ -540,7 +540,7 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
+ " did not reference FetchableContainer" + " did not reference FetchableContainer"
); );
} }
navigablePath = fetchParentMemento.getNavigablePath().append( propertyPathParts[ i ] ); navigablePath = navigablePath.append( propertyPathParts[ i ] );
fetchable = (Fetchable) ( (FetchableContainer) fetchable ).findSubPart( propertyPathParts[i], null ); fetchable = (Fetchable) ( (FetchableContainer) fetchable ).findSubPart( propertyPathParts[i], null );
} }

View File

@ -18,17 +18,22 @@ import javax.persistence.FieldResult;
import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMapping;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.RuntimeMetamodels; import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.internal.FetchMementoBasicStandard; import org.hibernate.query.internal.FetchMementoBasicStandard;
import org.hibernate.query.internal.ImplicitAttributeFetchMemento;
import org.hibernate.query.internal.ModelPartResultMementoBasicImpl; import org.hibernate.query.internal.ModelPartResultMementoBasicImpl;
import org.hibernate.query.internal.NamedResultSetMappingMementoImpl; import org.hibernate.query.internal.NamedResultSetMappingMementoImpl;
import org.hibernate.query.internal.ResultMementoBasicStandard; import org.hibernate.query.internal.ResultMementoBasicStandard;
@ -41,7 +46,8 @@ import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.named.ResultMemento; import org.hibernate.query.named.ResultMemento;
import org.hibernate.query.named.ResultMementoBasic; import org.hibernate.query.named.ResultMementoBasic;
import org.hibernate.query.named.ResultMementoInstantiation.ArgumentMemento; import org.hibernate.query.named.ResultMementoInstantiation.ArgumentMemento;
import org.hibernate.query.results.complete.CompleteResultBuilderBasicModelPart; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/** /**
@ -330,36 +336,32 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final String entityName; private final String entityName;
private final String relativeFetchPath; private final String propertyPath;
private final String[] propertyPathParts;
private final List<String> columnNames; private final List<String> columnNames;
private AttributeFetchDescriptor( private AttributeFetchDescriptor(
NavigablePath entityPath, NavigablePath entityPath,
String entityName, String entityName,
String relativeFetchPath, String propertyPath,
String columnName) { String columnName) {
final String[] names = relativeFetchPath.split( "\\." );
NavigablePath fetchPath = entityPath;
//noinspection ForLoopReplaceableByForEach
for ( int i = 0; i < names.length; i++ ) {
fetchPath = fetchPath.append( names[ i ] );
}
this.navigablePath = fetchPath;
this.entityName = entityName; this.entityName = entityName;
this.relativeFetchPath = relativeFetchPath; this.propertyPath = propertyPath;
this.propertyPathParts = propertyPath.split( "\\." );
this.navigablePath = entityPath;
this.columnNames = new ArrayList<>(); this.columnNames = new ArrayList<>();
columnNames.add( columnName ); columnNames.add( columnName );
} }
private void addColumn(FieldResult fieldResult) { private void addColumn(FieldResult fieldResult) {
if ( ! relativeFetchPath.equals( fieldResult.name() ) ) { if ( ! propertyPath.equals( fieldResult.name() ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"Passed FieldResult [%s, %s] does not match AttributeFetchMapping [%s]", "Passed FieldResult [%s, %s] does not match AttributeFetchMapping [%s]",
fieldResult.name(), fieldResult.name(),
fieldResult.column(), fieldResult.column(),
relativeFetchPath propertyPath
) )
); );
} }
@ -374,7 +376,7 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels(); final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels();
final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName ); final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName );
final ModelPart subPart = entityMapping.findSubPart( relativeFetchPath, null ); final ModelPart subPart = entityMapping.findSubPart( propertyPath, null );
//noinspection StatementWithEmptyBody //noinspection StatementWithEmptyBody
if ( subPart == null ) { if ( subPart == null ) {
@ -389,7 +391,7 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
} }
throw new NotYetImplementedFor6Exception( throw new NotYetImplementedFor6Exception(
"Only support for basic-valued model-parts have been implemented : " + relativeFetchPath "Only support for basic-valued model-parts have been implemented : " + propertyPath
+ " [" + subPart + "]" + " [" + subPart + "]"
); );
} }
@ -399,15 +401,34 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels(); final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels();
final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName ); final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName );
final ModelPart subPart = entityMapping.resolveSubPart( navigablePath ); NavigablePath navigablePath = this.navigablePath.append( propertyPathParts[ 0 ] );
ModelPart subPart = entityMapping.findSubPart(
propertyPathParts[ 0 ],
null
);
for ( int i = 1; i < propertyPathParts.length; i++ ) {
if ( ! ( subPart instanceof ModelPartContainer ) ) {
throw new MappingException(
"Non-terminal property path [" + navigablePath.getFullPath()
+ " did not reference FetchableContainer"
);
}
navigablePath = navigablePath.append( propertyPathParts[ i ] );
subPart = ( (ModelPartContainer) subPart ).findSubPart( propertyPathParts[i], null );
}
if ( subPart instanceof BasicValuedModelPart ) { if ( subPart instanceof BasicValuedModelPart ) {
assert columnNames.size() == 1; assert columnNames.size() == 1;
final BasicValuedModelPart basicPart = (BasicValuedModelPart) subPart; final BasicValuedModelPart basicPart = (BasicValuedModelPart) subPart;
return new FetchMementoBasicStandard( navigablePath, basicPart, columnNames.get( 0 ) ); return new FetchMementoBasicStandard( navigablePath, basicPart, columnNames.get( 0 ) );
} }
else if ( subPart instanceof EntityValuedModelPart ) {
return new ImplicitAttributeFetchMemento( navigablePath, (AttributeMapping) subPart );
}
throw new NotYetImplementedFor6Exception( throw new NotYetImplementedFor6Exception(
"Only support for basic-valued model-parts have been implemented : " + relativeFetchPath "Only support for basic-valued model-parts have been implemented : " + propertyPath
+ " [" + subPart + "]" + " [" + subPart + "]"
); );
} }

View File

@ -23,6 +23,7 @@ import org.hibernate.annotations.CacheModeType;
import org.hibernate.annotations.FlushModeType; import org.hibernate.annotations.FlushModeType;
import org.hibernate.annotations.QueryHints; import org.hibernate.annotations.QueryHints;
import org.hibernate.boot.internal.NamedHqlQueryDefinitionImpl; import org.hibernate.boot.internal.NamedHqlQueryDefinitionImpl;
import org.hibernate.boot.internal.NamedProcedureCallDefinitionImpl;
import org.hibernate.boot.query.NamedNativeQueryDefinitionBuilder; import org.hibernate.boot.query.NamedNativeQueryDefinitionBuilder;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.query.NamedHqlQueryDefinition; import org.hibernate.boot.query.NamedHqlQueryDefinition;
@ -316,17 +317,15 @@ public abstract class QueryBinder {
throw new AnnotationException( "A named query must have a name when used in class or package level" ); throw new AnnotationException( "A named query must have a name when used in class or package level" );
} }
throw new NotYetImplementedFor6Exception(); final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation );
// NamedProcedureCallDefinition.
// final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation ); if ( isDefault ) {
// context.getMetadataCollector().addDefaultNamedProcedureCall( def );
// if (isDefault) { }
// context.getMetadataCollector().addDefaultNamedProcedureCall( def ); else {
// } context.getMetadataCollector().addNamedProcedureCallDefinition( def );
// else { }
// context.getMetadataCollector().addNamedProcedureCallDefinition( def ); LOG.debugf( "Bound named stored procedure query : %s => %s", def.getRegistrationName(), def.getProcedureName() );
// }
// LOG.debugf( "Bound named stored procedure query : %s => %s", def.getRegistrationName(), def.getProcedureName() );
} }
public static void bindSqlResultSetMappings( public static void bindSqlResultSetMappings(

View File

@ -72,8 +72,8 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new BagInitializerProducer( return new BagInitializerProducer(
attributeMapping, attributeMapping,
attributeMapping.getIdentifierDescriptor() == null ? null : attributeMapping.getIdentifierDescriptor().generateFetch( attributeMapping.getIdentifierDescriptor() == null ? null : fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIdentifierDescriptor(),
navigablePath.append( CollectionPart.Nature.ID.getName() ), navigablePath.append( CollectionPart.Nature.ID.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -81,8 +81,8 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
null, null,
creationState creationState
), ),
attributeMapping.getElementDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -105,8 +105,8 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
Fetch elementFetch, Fetch elementFetch,
DomainResultCreationState creationState){ DomainResultCreationState creationState){
if ( indexFetch == null ) { if ( indexFetch == null ) {
indexFetch = attributeMapping.getIdentifierDescriptor() == null ? null : attributeMapping.getIdentifierDescriptor().generateFetch( indexFetch = attributeMapping.getIdentifierDescriptor() == null ? null : fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIdentifierDescriptor(),
navigablePath.append( CollectionPart.Nature.ID.getName() ), navigablePath.append( CollectionPart.Nature.ID.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -116,8 +116,8 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
); );
} }
if ( elementFetch == null ) { if ( elementFetch == null ) {
elementFetch = attributeMapping.getElementDescriptor().generateFetch( elementFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,

View File

@ -84,8 +84,8 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new MapInitializerProducer( return new MapInitializerProducer(
attributeMapping, attributeMapping,
attributeMapping.getIndexDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -93,8 +93,8 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
null, null,
creationState creationState
), ),
attributeMapping.getElementDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -117,8 +117,8 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
Fetch elementFetch, Fetch elementFetch,
DomainResultCreationState creationState){ DomainResultCreationState creationState){
if ( indexFetch == null ) { if ( indexFetch == null ) {
indexFetch = attributeMapping.getIndexDescriptor().generateFetch( indexFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -128,8 +128,8 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
); );
} }
if ( elementFetch == null ) { if ( elementFetch == null ) {
elementFetch = attributeMapping.getElementDescriptor().generateFetch( elementFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,

View File

@ -57,8 +57,8 @@ public abstract class AbstractSetSemantics<SE extends Set<E>,E> implements Colle
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new SetInitializerProducer( return new SetInitializerProducer(
attributeMapping, attributeMapping,
attributeMapping.getElementDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,

View File

@ -105,8 +105,8 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new ArrayInitializerProducer( return new ArrayInitializerProducer(
attributeMapping, attributeMapping,
attributeMapping.getIndexDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -114,8 +114,8 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
null, null,
creationState creationState
), ),
attributeMapping.getElementDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -138,8 +138,8 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
Fetch elementFetch, Fetch elementFetch,
DomainResultCreationState creationState){ DomainResultCreationState creationState){
if ( indexFetch == null ) { if ( indexFetch == null ) {
indexFetch = attributeMapping.getIndexDescriptor().generateFetch( indexFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -149,8 +149,8 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
); );
} }
if ( elementFetch == null ) { if ( elementFetch == null ) {
elementFetch = attributeMapping.getElementDescriptor().generateFetch( elementFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,

View File

@ -79,8 +79,8 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new ListInitializerProducer( return new ListInitializerProducer(
attributeMapping, attributeMapping,
attributeMapping.getIndexDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -88,8 +88,8 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
null, null,
creationState creationState
), ),
attributeMapping.getElementDescriptor().generateFetch( fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -112,8 +112,8 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
Fetch elementFetch, Fetch elementFetch,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( indexFetch == null ) { if ( indexFetch == null ) {
indexFetch = attributeMapping.getIndexDescriptor().generateFetch( indexFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ), navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
@ -123,8 +123,8 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
); );
} }
if ( elementFetch == null ) { if ( elementFetch == null ) {
elementFetch = attributeMapping.getElementDescriptor().generateFetch( elementFetch = fetchParent.generateFetchableFetch(
fetchParent, attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,

View File

@ -102,8 +102,8 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
fetchParent.getReferencedMappingContainer().visitFetchables( fetchParent.getReferencedMappingContainer().visitFetchables(
fetchable -> { fetchable -> {
final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() ); final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
final Fetch fetch = fetchable.generateFetch( final Fetch fetch = fetchParent.generateFetchableFetch(
fetchParent, fetchable,
navigablePath, navigablePath,
fetchable.getMappedFetchOptions().getTiming(), fetchable.getMappedFetchOptions().getTiming(),
true, true,
@ -322,8 +322,8 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
fetchParent.getReferencedMappingContainer().visitFetchables( fetchParent.getReferencedMappingContainer().visitFetchables(
(fetchable) -> { (fetchable) -> {
final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() ); final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
final Fetch fetch = fetchable.generateFetch( final Fetch fetch = fetchParent.generateFetchableFetch(
fetchParent, fetchable,
navigablePath, navigablePath,
fetchable.getMappedFetchOptions().getTiming(), fetchable.getMappedFetchOptions().getTiming(),
true, true,

View File

@ -737,8 +737,8 @@ public class LoaderSelectBuilder {
if ( changeFetchDepth ) { if ( changeFetchDepth ) {
fetchDepth++; fetchDepth++;
} }
final Fetch fetch = fetchable.generateFetch( final Fetch fetch = fetchParent.generateFetchableFetch(
fetchParent, fetchable,
fetchablePath, fetchablePath,
fetchTiming, fetchTiming,
joined, joined,

View File

@ -19,6 +19,8 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract; import org.hibernate.SharedSessionContract;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
@ -35,10 +37,13 @@ import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping; import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl; import org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl;
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -165,6 +170,67 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
} }
private EmbeddableMappingType(
EmbeddableValuedModelPart valueMapping,
SelectableMappings selectableMappings,
EmbeddableMappingType inverseMappingType,
MappingModelCreationProcess creationProcess) {
this.embeddableJtd = null;
this.representationStrategy = inverseMappingType.representationStrategy;
this.sessionFactory = inverseMappingType.sessionFactory;
this.valueMapping = valueMapping;
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
this.selectableMappings = selectableMappings;
creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization",
() -> {
if ( inverseMappingType.attributeMappings.isEmpty() ) {
return false;
}
int currentIndex = 0;
// We copy the attributes from the inverse mappings and replace the selection mappings
for ( AttributeMapping attributeMapping : inverseMappingType.attributeMappings ) {
if ( attributeMapping instanceof BasicValuedSingularAttributeMapping ) {
final BasicValuedSingularAttributeMapping original = (BasicValuedSingularAttributeMapping) attributeMapping;
final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex );
attributeMapping = BasicValuedSingularAttributeMapping.withSelectableMapping( original, selectableMapping );
currentIndex++;
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping;
final ToOneAttributeMapping toOne = original.copy();
final int offset = currentIndex;
toOne.setIdentifyingColumnsTableExpression(
selectableMappings.getSelectable( offset ).getContainingTableExpression()
);
toOne.setForeignKeyDescriptor(
original.getForeignKeyDescriptor().withKeySelectionMapping(
index -> selectableMappings.getSelectable( offset + index ),
creationProcess
)
);
attributeMapping = toOne;
currentIndex += attributeMapping.getJdbcTypeCount();
}
else {
throw new UnsupportedOperationException(
"Only basic and to-one attributes are supported in composite fks" );
}
this.attributeMappings.add( attributeMapping );
}
return true;
}
);
}
public EmbeddableMappingType createInverseMappingType(
EmbeddableValuedModelPart valueMapping,
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
return new EmbeddableMappingType( valueMapping, selectableMappings, this, creationProcess );
}
private boolean finishInitialization( private boolean finishInitialization(
Component bootDescriptor, Component bootDescriptor,
CompositeType compositeType, CompositeType compositeType,

View File

@ -6,7 +6,12 @@
*/ */
package org.hibernate.metamodel.mapping; package org.hibernate.metamodel.mapping;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -27,6 +32,8 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
String getTargetTable(); String getTargetTable();
ModelPart getKeyPart();
DomainResult createCollectionFetchDomainResult( DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath, NavigablePath collectionPath,
TableGroup tableGroup, TableGroup tableGroup,
@ -84,5 +91,12 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
return visitTargetSelectables( 0, consumer ); return visitTargetSelectables( 0, consumer );
} }
/**
* Return a copy of this foreign key descriptor with the selectable mappings as provided by the given accessor.
*/
ForeignKeyDescriptor withKeySelectionMapping(
IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess);
AssociationKey getAssociationKey(); AssociationKey getAssociationKey();
} }

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.query.SortOrder; import org.hibernate.query.SortOrder;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
@ -16,8 +19,10 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ordering.ast.DomainPath; import org.hibernate.metamodel.mapping.ordering.ast.DomainPath;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -29,6 +34,77 @@ import org.hibernate.sql.ast.tree.select.SortSpecification;
public abstract class AbstractDomainPath implements DomainPath { public abstract class AbstractDomainPath implements DomainPath {
public static final String ELEMENT_TOKEN = "$element$"; public static final String ELEMENT_TOKEN = "$element$";
@Override
public SqlAstNode resolve(
QuerySpec ast,
TableGroup tableGroup,
String modelPartName,
SqlAstCreationState creationState) {
return resolve(
getReferenceModelPart(),
ast,
tableGroup,
modelPartName,
creationState
);
}
public Expression resolve(
ModelPart referenceModelPart,
QuerySpec ast,
TableGroup tableGroup,
String modelPartName,
SqlAstCreationState creationState) {
if ( referenceModelPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart selection = (BasicValuedModelPart) referenceModelPart;
final TableReference tableReference = tableGroup.resolveTableReference( selection.getContainingTableExpression() );
return creationState.getSqlExpressionResolver().resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
selection.getContainingTableExpression(),
selection.getSelectionExpression()
),
sqlAstProcessingState -> new ColumnReference(
tableReference,
selection,
creationState.getCreationContext().getSessionFactory()
)
);
}
else if ( referenceModelPart instanceof EntityValuedModelPart ) {
final ModelPart subPart;
if ( ELEMENT_TOKEN.equals( modelPartName ) ) {
subPart = ( (EntityValuedModelPart) referenceModelPart ).getEntityMappingType().getIdentifierMapping();
}
else {
subPart = ( (EntityValuedModelPart) referenceModelPart ).findSubPart( modelPartName );
}
return resolve( subPart, ast, tableGroup, modelPartName, creationState );
}
else if ( referenceModelPart instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) referenceModelPart;
if ( embeddableValuedModelPart.getFetchableName()
.equals( modelPartName ) || ELEMENT_TOKEN.equals( modelPartName ) ) {
final List<Expression> expressions = new ArrayList<>( embeddableValuedModelPart.getNumberOfFetchables() );
embeddableValuedModelPart.visitFetchables(
fetchable -> {
expressions.add( resolve( fetchable, ast, tableGroup, modelPartName, creationState ) );
},
null
);
return new SqlTuple( expressions, embeddableValuedModelPart );
}
else {
ModelPart subPart = embeddableValuedModelPart.findSubPart( modelPartName, null );
assert subPart instanceof BasicValuedModelPart;
return resolve( subPart, ast, tableGroup, modelPartName, creationState );
}
}
else {
// sure it can happen
throw new NotYetImplementedFor6Exception( "Ordering for " + referenceModelPart + " not supported" );
}
}
@Override @Override
public void apply( public void apply(
QuerySpec ast, QuerySpec ast,
@ -97,7 +173,7 @@ public abstract class AbstractDomainPath implements DomainPath {
} }
else { else {
// sure it can happen // sure it can happen
throw new NotYetImplementedFor6Exception( "Ordering for " + getReferenceModelPart() + "not supported" ); throw new NotYetImplementedFor6Exception( "Ordering for " + getReferenceModelPart() + " not supported" );
} }
} }

View File

@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
@ -101,6 +102,51 @@ public class BasicValuedSingularAttributeMapping
} }
} }
public static BasicValuedSingularAttributeMapping withSelectableMapping(
BasicValuedModelPart original,
SelectableMapping selectableMapping) {
String attributeName = null;
int stateArrayPosition = 0;
StateArrayContributorMetadataAccess attributeMetadataAccess = null;
BasicValueConverter<?, ?> valueConverter = null;
PropertyAccess propertyAccess = null;
ManagedMappingType declaringType = null;
if ( original instanceof SingleAttributeIdentifierMapping ) {
final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original;
attributeName = mapping.getAttributeName();
attributeMetadataAccess = null;
propertyAccess = mapping.getPropertyAccess();
declaringType = mapping.findContainingEntityMapping();
}
else if ( original instanceof SingularAttributeMapping ) {
final SingularAttributeMapping mapping = (SingularAttributeMapping) original;
attributeName = mapping.getAttributeName();
stateArrayPosition = mapping.getStateArrayPosition();
attributeMetadataAccess = mapping.getAttributeMetadataAccess();
propertyAccess = mapping.getPropertyAccess();
declaringType = mapping.getDeclaringType();
}
if ( original instanceof ConvertibleModelPart ) {
valueConverter = ( (ConvertibleModelPart) original ).getValueConverter();
}
return new BasicValuedSingularAttributeMapping(
attributeName,
original.getNavigableRole(),
stateArrayPosition,
attributeMetadataAccess,
FetchStrategy.IMMEDIATE_JOIN,
selectableMapping.getContainingTableExpression(),
selectableMapping.getSelectionExpression(),
selectableMapping.isFormula(),
selectableMapping.getCustomReadExpression(),
selectableMapping.getCustomWriteExpression(),
valueConverter,
selectableMapping.getJdbcMapping(),
declaringType,
propertyAccess
);
}
@Override @Override
public JdbcMapping getJdbcMapping() { public JdbcMapping getJdbcMapping() {
return jdbcMapping; return jdbcMapping;

View File

@ -16,11 +16,13 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
@ -99,6 +101,31 @@ public class EmbeddedAttributeMapping
this.embeddableMappingType = embeddableMappingType; this.embeddableMappingType = embeddableMappingType;
} }
// Constructor is only used for creating the inverse attribute mapping
private EmbeddedAttributeMapping(
SelectableMappings selectableMappings,
EmbeddableValuedModelPart inverseModelPart,
MappingModelCreationProcess creationProcess) {
super( inverseModelPart.getFetchableName(), -1, null, inverseModelPart.getMappedFetchOptions(), null, null );
this.navigableRole = inverseModelPart.getNavigableRole().getParent().append( inverseModelPart.getFetchableName() );
this.tableExpression = selectableMappings.getSelectable( 0 ).getContainingTableExpression();
this.embeddableMappingType = inverseModelPart.getEmbeddableTypeDescriptor().createInverseMappingType(
this,
selectableMappings,
creationProcess
);
this.parentInjectionAttributePropertyAccess = null;
}
public static EmbeddableValuedModelPart createInverseModelPart(
EmbeddableValuedModelPart modelPart,
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
return new EmbeddedAttributeMapping( selectableMappings, modelPart, creationProcess );
}
@Override @Override
public EmbeddableMappingType getMappedType() { public EmbeddableMappingType getMappedType() {
return getEmbeddableTypeDescriptor(); return getEmbeddableTypeDescriptor();

View File

@ -8,8 +8,11 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import org.hibernate.HibernateException; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer; import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
@ -19,7 +22,9 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
@ -27,11 +32,10 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
@ -47,7 +51,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
*/ */
public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
private final EmbeddableValuedModelPart mappingType; private final EmbeddableValuedModelPart keyMappingType;
private final EmbeddableValuedModelPart targetMappingType;
private final String keyTable; private final String keyTable;
private final SelectableMappings keySelectableMappings; private final SelectableMappings keySelectableMappings;
private final String targetTable; private final String targetTable;
@ -55,7 +60,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
private AssociationKey associationKey; private AssociationKey associationKey;
public EmbeddedForeignKeyDescriptor( public EmbeddedForeignKeyDescriptor(
EmbeddableValuedModelPart mappingType, EmbeddableValuedModelPart targetMappingType,
String keyTable, String keyTable,
SelectableMappings keySelectableMappings, SelectableMappings keySelectableMappings,
String targetTable, String targetTable,
@ -65,24 +70,45 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
this.keySelectableMappings = keySelectableMappings; this.keySelectableMappings = keySelectableMappings;
this.targetTable = targetTable; this.targetTable = targetTable;
this.targetSelectableMappings = targetSelectableMappings; this.targetSelectableMappings = targetSelectableMappings;
this.mappingType = mappingType; this.targetMappingType = targetMappingType;
this.keyMappingType = EmbeddedAttributeMapping.createInverseModelPart(
targetMappingType,
keySelectableMappings,
creationProcess
);
creationProcess.registerInitializationCallback( creationProcess.registerInitializationCallback(
"Embedded (composite) FK descriptor " + mappingType.getNavigableRole(), "Embedded (composite) FK descriptor " + targetMappingType.getNavigableRole(),
() -> { () -> {
// todo (6.0) : how to make sure things we need are ready to go? // todo (6.0) : how to make sure things we need are ready to go?
// - e.g., here, we need access to the sub-attributes // - e.g., here, we need access to the sub-attributes
final List<AttributeMapping> subAttributes = mappingType.getEmbeddableTypeDescriptor().getAttributeMappings(); final List<AttributeMapping> subAttributes = targetMappingType.getEmbeddableTypeDescriptor().getAttributeMappings();
if ( subAttributes.isEmpty() ) { if ( subAttributes.isEmpty() ) {
// todo (6.0) : ^^ for now, this is the only way we "know" that the embeddable has not been finalized yet // todo (6.0) : ^^ for now, this is the only way we "know" that the embeddable has not been finalized yet
return false; return false;
} }
return true; return true;
} }
); );
} }
private EmbeddedForeignKeyDescriptor(
EmbeddedForeignKeyDescriptor original,
String keyTable,
SelectableMappings keySelectableMappings,
MappingModelCreationProcess creationProcess) {
this.keyTable = keyTable;
this.keySelectableMappings = keySelectableMappings;
this.targetTable = original.targetTable;
this.targetSelectableMappings = original.targetSelectableMappings;
this.targetMappingType = original.targetMappingType;
this.keyMappingType = EmbeddedAttributeMapping.createInverseModelPart(
targetMappingType,
keySelectableMappings,
creationProcess
);
}
@Override @Override
public String getKeyTable() { public String getKeyTable() {
return keyTable; return keyTable;
@ -93,6 +119,22 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return targetTable; return targetTable;
} }
@Override
public ForeignKeyDescriptor withKeySelectionMapping(
IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess) {
SelectableMapping[] selectionMappings = new SelectableMapping[keySelectableMappings.getJdbcTypeCount()];
for ( int i = 0; i < selectionMappings.length; i++ ) {
selectionMappings[i] = selectableMappingAccess.apply( i );
}
return new EmbeddedForeignKeyDescriptor(
this,
selectionMappings[0].getContainingTableExpression(),
new SelectableMappingsImpl( selectionMappings ),
creationProcess
);
}
@Override @Override
public DomainResult createCollectionFetchDomainResult( public DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath, NavigablePath collectionPath,
@ -103,7 +145,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
collectionPath, collectionPath,
tableGroup, tableGroup,
targetTable, targetTable,
targetSelectableMappings, targetMappingType,
creationState creationState
); );
} }
@ -112,7 +154,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
collectionPath, collectionPath,
tableGroup, tableGroup,
keyTable, keyTable,
keySelectableMappings, keyMappingType,
creationState creationState
); );
} }
@ -120,84 +162,70 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public DomainResult createDomainResult( public DomainResult createDomainResult(
NavigablePath collectionPath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return createDomainResult( return createDomainResult(
collectionPath, navigablePath,
tableGroup, tableGroup,
keyTable, keyTable,
keySelectableMappings, keyMappingType,
creationState creationState
); );
} }
@Override @Override
public DomainResult createDomainResult( public DomainResult createDomainResult(
NavigablePath collectionPath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
boolean isKeyReferringSide, boolean isKeyReferringSide,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( isKeyReferringSide ) { if ( isKeyReferringSide ) {
return createDomainResult( return createDomainResult(
collectionPath, navigablePath,
tableGroup, tableGroup,
keyTable, keyTable,
keySelectableMappings, keyMappingType,
creationState creationState
); );
} }
else { else {
return createDomainResult( return createDomainResult(
collectionPath, navigablePath,
tableGroup, tableGroup,
targetTable, targetTable,
targetSelectableMappings, targetMappingType,
creationState creationState
); );
} }
} }
private DomainResult createDomainResult( private DomainResult createDomainResult(
NavigablePath collectionPath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
String columnContainingTable, String columnContainingTable,
SelectableMappings selectableMappings, EmbeddableValuedModelPart modelPart,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final NavigablePath fkNavigablePath = navigablePath.append( getPartName() );
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
final TableReference tableReference = tableGroup.resolveTableReference( columnContainingTable ); fkNavigablePath,
final String identificationVariable = tableReference.getIdentificationVariable(); np -> {
final TableGroupJoin tableGroupJoin = modelPart.createTableGroupJoin(
final List<SqlSelection> sqlSelections = new ArrayList<>( selectableMappings.getJdbcTypeCount() ); fkNavigablePath,
selectableMappings.forEachSelectable( tableGroup,
(columnIndex, selection) -> { null,
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( SqlAstJoinType.INNER,
sqlExpressionResolver.resolveSqlExpression( LockMode.NONE,
SqlExpressionResolver.createColumnReferenceKey( creationState.getSqlAstCreationState()
tableReference,
selection.getSelectionExpression()
),
s ->
new ColumnReference(
identificationVariable,
selection,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
selection.getJdbcMapping().getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
); );
sqlSelections.add( sqlSelection ); return tableGroupJoin.getJoinedGroup();
} }
); );
return new EmbeddableForeignKeyResultImpl<>( return new EmbeddableForeignKeyResultImpl<>(
sqlSelections, navigablePath,
collectionPath, modelPart,
mappingType,
null, null,
creationState creationState
); );
@ -344,19 +372,24 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return associationKey; return associationKey;
} }
@Override
public ModelPart getKeyPart() {
return keyMappingType.getEmbeddableTypeDescriptor().getEmbeddedValueMapping();
}
@Override @Override
public MappingType getPartMappingType() { public MappingType getPartMappingType() {
throw new HibernateException( "Unexpected call to SimpleForeignKeyDescriptor#getPartMappingType" ); return targetMappingType.getPartMappingType();
} }
@Override @Override
public JavaTypeDescriptor<?> getJavaTypeDescriptor() { public JavaTypeDescriptor<?> getJavaTypeDescriptor() {
return mappingType.getJavaTypeDescriptor(); return targetMappingType.getJavaTypeDescriptor();
} }
@Override @Override
public NavigableRole getNavigableRole() { public NavigableRole getNavigableRole() {
throw new UnsupportedOperationException(); return targetMappingType.getNavigableRole();
} }
@Override @Override
@ -365,41 +398,26 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
//noinspection unchecked final NavigablePath fkNavigablePath = navigablePath.append( getPartName() );
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); fkNavigablePath,
final TableReference tableReference = tableGroup.resolveTableReference( keyTable ); np -> {
final String identificationVariable = tableReference.getIdentificationVariable(); final TableGroupJoin tableGroupJoin = keyMappingType.createTableGroupJoin(
final int size = keySelectableMappings.getJdbcTypeCount(); fkNavigablePath,
final List<SqlSelection> sqlSelections = new ArrayList<>( size ); tableGroup,
keySelectableMappings.forEachSelectable( null,
(columnIndex, selection) -> { null,
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( LockMode.NONE,
sqlExpressionResolver.resolveSqlExpression( creationState.getSqlAstCreationState()
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selection.getSelectionExpression()
),
s ->
new ColumnReference(
identificationVariable,
selection,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
selection.getJdbcMapping().getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
); );
sqlSelections.add( sqlSelection ); return tableGroupJoin.getJoinedGroup();
} }
); );
return new EmbeddableForeignKeyResultImpl<>( return new EmbeddableForeignKeyResultImpl<>(
sqlSelections,
navigablePath, navigablePath,
mappingType, keyMappingType,
resultVariable, resultVariable,
creationState creationState
); );
@ -419,8 +437,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public Object getAssociationKeyFromTarget(Object targetObject, SharedSessionContractImplementor session) { public Object getAssociationKeyFromTarget(Object targetObject, SharedSessionContractImplementor session) {
// If the mapping type has an identifier type, that identifier is the key // If the mapping type has an identifier type, that identifier is the key
if ( mappingType instanceof SingleAttributeIdentifierMapping ) { if ( targetMappingType instanceof SingleAttributeIdentifierMapping ) {
return ( (SingleAttributeIdentifierMapping) mappingType ).getIdentifier( targetObject, session ); return ( (SingleAttributeIdentifierMapping) targetMappingType ).getIdentifier( targetObject, session );
} }
// Otherwise this is a key based on the target object i.e. without id-class // Otherwise this is a key based on the target object i.e. without id-class
return targetObject; return targetObject;
@ -428,12 +446,12 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public EntityMappingType findContainingEntityMapping() { public EntityMappingType findContainingEntityMapping() {
throw new UnsupportedOperationException(); return targetMappingType.findContainingEntityMapping();
} }
@Override @Override
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) { public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
return mappingType.forEachJdbcType( offset, action ); return targetMappingType.forEachJdbcType( offset, action );
} }
@Override @Override
@ -443,11 +461,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
int offset, int offset,
JdbcValuesConsumer valuesConsumer, JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
return mappingType.forEachDisassembledJdbcValue( value, clause, offset, valuesConsumer, session ); return targetMappingType.forEachDisassembledJdbcValue( value, clause, offset, valuesConsumer, session );
} }
@Override @Override
public Object disassemble(Object value, SharedSessionContractImplementor session) { public Object disassemble(Object value, SharedSessionContractImplementor session) {
return mappingType.disassemble( value, session ); return targetMappingType.disassemble( value, session );
} }
} }

View File

@ -8,7 +8,9 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.IntFunction;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
@ -17,17 +19,16 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer; import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.ValueMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
@ -57,8 +58,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicValuedModelPart, FetchOptions { public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicValuedModelPart, FetchOptions {
private final SideModelPart keySide; private final BasicValuedModelPart keySide;
private final SideModelPart targetSide; private final BasicValuedModelPart targetSide;
private final boolean refersToPrimaryKey; private final boolean refersToPrimaryKey;
@ -68,45 +69,57 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
public SimpleForeignKeyDescriptor( public SimpleForeignKeyDescriptor(
SelectableMapping keySelectableMapping, SelectableMapping keySelectableMapping,
SelectableMapping targetSelectableMapping, BasicValuedModelPart targetModelPart,
Function<Object,Object> disassemblyValueExtractor, Function<Object, Object> disassemblyValueExtractor,
boolean refersToPrimaryKey) { boolean refersToPrimaryKey) {
assert keySelectableMapping != null; assert keySelectableMapping != null;
assert targetSelectableMapping != null; assert targetModelPart != null;
assert disassemblyValueExtractor != null; assert disassemblyValueExtractor != null;
this.keySide = new SideModelPart( keySelectableMapping ); this.keySide = BasicValuedSingularAttributeMapping.withSelectableMapping( targetModelPart, keySelectableMapping );
this.targetSide = new SideModelPart( targetSelectableMapping ); this.targetSide = targetModelPart;
this.disassemblyValueExtractor = disassemblyValueExtractor; this.disassemblyValueExtractor = disassemblyValueExtractor;
this.refersToPrimaryKey = refersToPrimaryKey; this.refersToPrimaryKey = refersToPrimaryKey;
} }
@Override @Override
public String getKeyTable() { public String getKeyTable() {
return keySide.selectableMapping.getContainingTableExpression(); return keySide.getContainingTableExpression();
} }
@Override @Override
public String getTargetTable() { public String getTargetTable() {
return targetSide.selectableMapping.getContainingTableExpression(); return targetSide.getContainingTableExpression();
} }
public SideModelPart getKeySide() { public BasicValuedModelPart getKeySide() {
return keySide; return keySide;
} }
public SideModelPart getTargetSide() { public BasicValuedModelPart getTargetSide() {
return targetSide; return targetSide;
} }
@Override
public ForeignKeyDescriptor withKeySelectionMapping(
IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess) {
return new SimpleForeignKeyDescriptor(
selectableMappingAccess.apply( 0 ),
targetSide,
disassemblyValueExtractor,
refersToPrimaryKey
);
}
@Override @Override
public DomainResult<?> createCollectionFetchDomainResult( public DomainResult<?> createCollectionFetchDomainResult(
NavigablePath collectionPath, NavigablePath collectionPath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( targetSide.selectableMapping.getContainingTableExpression() if ( targetSide.getContainingTableExpression()
.equals( keySide.selectableMapping.getContainingTableExpression() ) ) { .equals( keySide.getContainingTableExpression() ) ) {
return createDomainResult( tableGroup, targetSide.selectableMapping, creationState ); return createDomainResult( collectionPath, tableGroup, targetSide, creationState );
} }
return createDomainResult( collectionPath, tableGroup, creationState ); return createDomainResult( collectionPath, tableGroup, creationState );
} }
@ -116,7 +129,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return createDomainResult( tableGroup, keySide.selectableMapping, creationState ); return createDomainResult( navigablePath, tableGroup, keySide, creationState );
} }
@Override @Override
@ -126,9 +139,9 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
boolean isKeyReferringSide, boolean isKeyReferringSide,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( isKeyReferringSide ) { if ( isKeyReferringSide ) {
return createDomainResult( tableGroup, keySide.selectableMapping, creationState ); return createDomainResult( navigablePath, tableGroup, keySide, creationState );
} }
return createDomainResult( tableGroup, targetSide.selectableMapping, creationState ); return createDomainResult( navigablePath, tableGroup, targetSide, creationState );
} }
@Override @Override
@ -137,10 +150,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return createDomainResult( tableGroup, keySide.selectableMapping, creationState ); return createDomainResult( navigablePath, tableGroup, keySide, creationState );
} }
private <T> DomainResult<T> createDomainResult( private <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
SelectableMapping selectableMapping, SelectableMapping selectableMapping,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
@ -180,17 +194,17 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
if ( lhs.getTableReference( keySide.selectableMapping.getContainingTableExpression() ) != null ) { if ( lhs.getTableReference( keySide.getContainingTableExpression() ) != null ) {
return new ComparisonPredicate( return new ComparisonPredicate(
new ColumnReference( new ColumnReference(
lhs, lhs,
keySide.selectableMapping, keySide,
creationContext.getSessionFactory() creationContext.getSessionFactory()
), ),
ComparisonOperator.EQUAL, ComparisonOperator.EQUAL,
new ColumnReference( new ColumnReference(
rhs, rhs,
targetSide.selectableMapping, targetSide,
creationContext.getSessionFactory() creationContext.getSessionFactory()
) )
); );
@ -199,13 +213,13 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
return new ComparisonPredicate( return new ComparisonPredicate(
new ColumnReference( new ColumnReference(
lhs, lhs,
targetSide.selectableMapping, targetSide,
creationContext.getSessionFactory() creationContext.getSessionFactory()
), ),
ComparisonOperator.EQUAL, ComparisonOperator.EQUAL,
new ColumnReference( new ColumnReference(
rhs, rhs,
keySide.selectableMapping, keySide,
creationContext.getSessionFactory() creationContext.getSessionFactory()
) )
); );
@ -221,22 +235,22 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
TableReference lhsTableReference; TableReference lhsTableReference;
TableReference rhsTableKeyReference; TableReference rhsTableKeyReference;
if ( targetSide.selectableMapping.getContainingTableExpression().equals( keySide.selectableMapping.getContainingTableExpression() ) ) { if ( targetSide.getContainingTableExpression().equals( keySide.getContainingTableExpression() ) ) {
lhsTableReference = getTableReferenceWhenTargetEqualsKey( lhs, tableGroup, keySide.selectableMapping.getContainingTableExpression() ); lhsTableReference = getTableReferenceWhenTargetEqualsKey( lhs, tableGroup, keySide.getContainingTableExpression() );
rhsTableKeyReference = getTableReference( rhsTableKeyReference = getTableReference(
lhs, lhs,
tableGroup, tableGroup,
targetSide.selectableMapping.getContainingTableExpression() targetSide.getContainingTableExpression()
); );
} }
else { else {
lhsTableReference = getTableReference( lhs, tableGroup, keySide.selectableMapping.getContainingTableExpression() ); lhsTableReference = getTableReference( lhs, tableGroup, keySide.getContainingTableExpression() );
rhsTableKeyReference = getTableReference( rhsTableKeyReference = getTableReference(
lhs, lhs,
tableGroup, tableGroup,
targetSide.selectableMapping.getContainingTableExpression() targetSide.getContainingTableExpression()
); );
} }
@ -282,6 +296,16 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" ); throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
} }
@Override
public ModelPart getKeyPart() {
return keySide;
}
@Override
public MappingType getPartMappingType() {
return targetSide.getMappedType();
}
@Override @Override
public JavaTypeDescriptor<?> getJavaTypeDescriptor() { public JavaTypeDescriptor<?> getJavaTypeDescriptor() {
return targetSide.getJdbcMapping().getJavaTypeDescriptor(); return targetSide.getJdbcMapping().getJavaTypeDescriptor();
@ -289,12 +313,12 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public NavigableRole getNavigableRole() { public NavigableRole getNavigableRole() {
throw new UnsupportedOperationException(); return targetSide.getNavigableRole();
} }
@Override @Override
public EntityMappingType findContainingEntityMapping() { public EntityMappingType findContainingEntityMapping() {
throw new UnsupportedOperationException(); return targetSide.findContainingEntityMapping();
} }
@Override @Override
@ -326,26 +350,26 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) { public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
valueConsumer.consume( domainValue, keySide.selectableMapping ); valueConsumer.consume( domainValue, keySide );
} }
@Override @Override
public int visitKeySelectables(int offset, SelectableConsumer consumer) { public int visitKeySelectables(int offset, SelectableConsumer consumer) {
consumer.accept( offset, keySide.selectableMapping ); consumer.accept( offset, keySide );
return getJdbcTypeCount(); return getJdbcTypeCount();
} }
@Override @Override
public int visitTargetSelectables(int offset, SelectableConsumer consumer) { public int visitTargetSelectables(int offset, SelectableConsumer consumer) {
consumer.accept( offset, targetSide.selectableMapping ); consumer.accept( offset, targetSide );
return getJdbcTypeCount(); return getJdbcTypeCount();
} }
@Override @Override
public AssociationKey getAssociationKey() { public AssociationKey getAssociationKey() {
if ( associationKey == null ) { if ( associationKey == null ) {
final List<String> associationKeyColumns = Collections.singletonList( keySide.selectableMapping.getSelectionExpression() ); final List<String> associationKeyColumns = Collections.singletonList( keySide.getSelectionExpression() );
associationKey = new AssociationKey( keySide.selectableMapping.getContainingTableExpression(), associationKeyColumns ); associationKey = new AssociationKey( keySide.getContainingTableExpression(), associationKeyColumns );
} }
return associationKey; return associationKey;
} }
@ -375,27 +399,27 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public String getContainingTableExpression() { public String getContainingTableExpression() {
return keySide.selectableMapping.getContainingTableExpression(); return keySide.getContainingTableExpression();
} }
@Override @Override
public String getSelectionExpression() { public String getSelectionExpression() {
return keySide.selectableMapping.getSelectionExpression(); return keySide.getSelectionExpression();
} }
@Override @Override
public boolean isFormula() { public boolean isFormula() {
return keySide.selectableMapping.isFormula(); return keySide.isFormula();
} }
@Override @Override
public String getCustomReadExpression() { public String getCustomReadExpression() {
return keySide.selectableMapping.getCustomReadExpression(); return keySide.getCustomReadExpression();
} }
@Override @Override
public String getCustomWriteExpression() { public String getCustomWriteExpression() {
return keySide.selectableMapping.getCustomWriteExpression(); return keySide.getCustomWriteExpression();
} }
@Override @Override
@ -444,70 +468,10 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
public String toString() { public String toString() {
return String.format( return String.format(
"SimpleForeignKeyDescriptor : %s.%s -> %s.%s", "SimpleForeignKeyDescriptor : %s.%s -> %s.%s",
keySide.selectableMapping.getContainingTableExpression(), keySide.getContainingTableExpression(),
keySide.selectableMapping.getSelectionExpression(), keySide.getSelectionExpression(),
targetSide.selectableMapping.getContainingTableExpression(), targetSide.getContainingTableExpression(),
targetSide.selectableMapping.getSelectionExpression() targetSide.getSelectionExpression()
); );
} }
private interface KeySideModelPart extends MappingModelExpressable, DomainResultProducer, SelectableMappings {
@Override
default int getJdbcTypeCount() {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
default List<JdbcMapping> getJdbcMappings() {
throw new NotYetImplementedFor6Exception( getClass() );
}
}
public static class SideModelPart implements BasicValuedMapping, KeySideModelPart {
private final SelectableMapping selectableMapping;
private final List<JdbcMapping> jdbcMappings;
public SideModelPart(SelectableMapping selectableMapping) {
assert selectableMapping != null;
this.selectableMapping = selectableMapping;
this.jdbcMappings = Collections.singletonList( getJdbcMapping() );
}
public SelectableMapping getSelectableMapping() {
return selectableMapping;
}
@Override
public int getJdbcTypeCount() {
return 1;
}
@Override
public List<JdbcMapping> getJdbcMappings() {
return jdbcMappings;
}
@Override
public JdbcMapping getJdbcMapping() {
return selectableMapping.getJdbcMapping();
}
@Override
public MappingType getMappedType() {
return getJdbcMapping();
}
@Override
public SelectableMapping getSelectable(int columnIndex) {
assert columnIndex == 0;
return selectableMapping;
}
@Override
public int forEachSelectable(int offset, SelectableConsumer consumer) {
consumer.accept( offset, selectableMapping );
return 1;
}
}
} }

View File

@ -16,12 +16,14 @@ import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
@ -48,6 +50,7 @@ import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable; import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.sql.results.graph.entity.EntityFetch;
@ -84,7 +87,7 @@ public class ToOneAttributeMapping
private final String referencedPropertyName; private final String referencedPropertyName;
private final Cardinality cardinality; private final Cardinality cardinality;
private String bidirectionalAttributeName; private final String bidirectionalAttributeName;
private ForeignKeyDescriptor foreignKeyDescriptor; private ForeignKeyDescriptor foreignKeyDescriptor;
private String identifyingColumnsTableExpression; private String identifyingColumnsTableExpression;
@ -122,6 +125,7 @@ public class ToOneAttributeMapping
else { else {
cardinality = Cardinality.MANY_TO_ONE; cardinality = Cardinality.MANY_TO_ONE;
} }
this.bidirectionalAttributeName = null;
} }
else { else {
assert bootValue instanceof OneToOne; assert bootValue instanceof OneToOne;
@ -171,22 +175,49 @@ public class ToOneAttributeMapping
so in order to recognize the bidirectionality the "primaryKey." is removed from the otherSidePropertyName value. so in order to recognize the bidirectionality the "primaryKey." is removed from the otherSidePropertyName value.
*/ */
// todo (6.0): find a better solution for the embeddable part name not in the NavigablePath // todo (6.0): find a better solution for the embeddable part name not in the NavigablePath
bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( String bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
( (OneToOne) bootValue ).getMappedByProperty(), ( (OneToOne) bootValue ).getMappedByProperty(),
'.' '.'
); );
if ( bidirectionalAttributeName == null ) { if ( bidirectionalAttributeName == null ) {
bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
bootValue.getReferencedPropertyName(), bootValue.getReferencedPropertyName(),
'.' '.'
); );
} }
else {
this.bidirectionalAttributeName = bidirectionalAttributeName;
}
} }
this.navigableRole = navigableRole; this.navigableRole = navigableRole;
} }
private ToOneAttributeMapping(ToOneAttributeMapping original) {
super(
original.getAttributeName(),
original.getStateArrayPosition(),
original.getAttributeMetadataAccess(),
original,
original.getDeclaringType(),
original.getPropertyAccess()
);
this.navigableRole = original.navigableRole;
this.sqlAliasStem = original.sqlAliasStem;
this.isNullable = original.isNullable;
this.unwrapProxy = original.unwrapProxy;
this.entityMappingType = original.entityMappingType;
this.referencedPropertyName = original.referencedPropertyName;
this.cardinality = original.cardinality;
this.bidirectionalAttributeName = original.bidirectionalAttributeName;
}
public ToOneAttributeMapping copy() {
return new ToOneAttributeMapping( this );
}
@Override
public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) { public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) {
isKeyReferringSide = foreignKeyDescriptor.getAssociationKey().getTable().equals( identifyingColumnsTableExpression ); isKeyReferringSide = foreignKeyDescriptor.getAssociationKey().getTable().equals( identifyingColumnsTableExpression );
assert identifyingColumnsTableExpression != null; assert identifyingColumnsTableExpression != null;
@ -197,6 +228,7 @@ public class ToOneAttributeMapping
identifyingColumnsTableExpression = tableExpression; identifyingColumnsTableExpression = tableExpression;
} }
@Override
public ForeignKeyDescriptor getForeignKeyDescriptor() { public ForeignKeyDescriptor getForeignKeyDescriptor() {
return this.foreignKeyDescriptor; return this.foreignKeyDescriptor;
} }
@ -205,6 +237,10 @@ public class ToOneAttributeMapping
return referencedPropertyName; return referencedPropertyName;
} }
public Cardinality getCardinality() {
return cardinality;
}
@Override @Override
public EntityMappingType getMappedType() { public EntityMappingType getMappedType() {
return getEntityMappingType(); return getEntityMappingType();
@ -230,6 +266,35 @@ public class ToOneAttributeMapping
if ( creationState.isAssociationKeyVisited( associationKey ) ) { if ( creationState.isAssociationKeyVisited( associationKey ) ) {
NavigablePath parentNavigablePath = fetchablePath.getParent(); NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals( fetchParent.getNavigablePath() ); assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
// The parent navigable path is {fk} if we are creating the domain result for the foreign key for a circular fetch
// In the following example, we create a circular fetch for the composite `Card.field.{id}.card.field`
// While creating the domain result for the foreign key of `Card#field`, we run into this condition
// We know that `Card#field` will be delayed because `EmbeddableForeignKeyResultImpl` enforces that
// so we can safely return null to avoid a stack overflow
/*
@Entity
public class Card {
@Id
private String id;
@ManyToOne
private CardField field;
}
@Entity
public class CardField {
@EmbeddedId
private PrimaryKey primaryKey;
}
@Embeddable
public class PrimaryKey {
@ManyToOne(optional = false)
private Card card;
@ManyToOne(optional = false)
private Key key;
}
*/
if ( parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) ) {
return null;
}
ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath ); ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath );
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) { if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
@ -407,6 +472,9 @@ public class ToOneAttributeMapping
fetchParent.getNavigablePath() fetchParent.getNavigablePath()
); );
final NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) { if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) {
if ( fetchParent instanceof EntityResultJoinedSubclassImpl && if ( fetchParent instanceof EntityResultJoinedSubclassImpl &&
( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) { ( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) {
@ -471,12 +539,11 @@ public class ToOneAttributeMapping
selectByUniqueKey = false; selectByUniqueKey = false;
} }
else { else {
// case 1.1
keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState ); keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState );
// case 1.1
selectByUniqueKey = true; selectByUniqueKey = true;
} }
assert !selected;
if ( fetchTiming == FetchTiming.IMMEDIATE ) { if ( fetchTiming == FetchTiming.IMMEDIATE ) {
return new EntityFetchSelectImpl( return new EntityFetchSelectImpl(
fetchParent, fetchParent,

View File

@ -15,6 +15,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.SortOrder; import org.hibernate.query.SortOrder;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
@ -57,19 +58,17 @@ public class ColumnReference implements OrderingExpression, SequencePart {
} }
@Override @Override
public void apply( public Expression resolve(
QuerySpec ast, QuerySpec ast,
TableGroup tableGroup, TableGroup tableGroup,
String collation,
String modelPartName, String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
TableReference tableReference; TableReference tableReference;
tableReference = getTableReference( tableGroup ); tableReference = getTableReference( tableGroup );
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver(); final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
final Expression expression = sqlExpressionResolver.resolveSqlExpression( return sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ), SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
sqlAstProcessingState -> new org.hibernate.sql.ast.tree.expression.ColumnReference( sqlAstProcessingState -> new org.hibernate.sql.ast.tree.expression.ColumnReference(
tableReference, tableReference,
@ -83,6 +82,17 @@ public class ColumnReference implements OrderingExpression, SequencePart {
creationState.getCreationContext().getSessionFactory() creationState.getCreationContext().getSessionFactory()
) )
); );
}
@Override
public void apply(
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final Expression expression = resolve( ast, tableGroup, modelPartName, creationState );
// It makes no sense to order by an expression multiple times // It makes no sense to order by an expression multiple times
// SQL Server even reports a query error in this case // SQL Server even reports a query error in this case
if ( ast.hasSortSpecifications() ) { if ( ast.hasSortSpecifications() ) {

View File

@ -11,17 +11,28 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.internal.AbstractDomainPath;
import org.hibernate.query.SortOrder; import org.hibernate.query.SortOrder;
import org.hibernate.query.sqm.function.FunctionRenderingSupport;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SortSpecification;
/** /**
* Represents a function used in an order-by fragment * Represents a function used in an order-by fragment
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class FunctionExpression implements OrderingExpression { public class FunctionExpression implements OrderingExpression, FunctionRenderingSupport {
private final String name; private final String name;
private final List<OrderingExpression> arguments; private final List<OrderingExpression> arguments;
@ -44,6 +55,42 @@ public class FunctionExpression implements OrderingExpression {
arguments.add( argument ); arguments.add( argument );
} }
@Override
public SelfRenderingFunctionSqlAstExpression resolve(
QuerySpec ast,
TableGroup tableGroup,
String modelPartName,
SqlAstCreationState creationState) {
final int size = arguments.size();
final List<SqlAstNode> args = new ArrayList<>( size );
for ( int i = 0; i < size; i++ ) {
final OrderingExpression orderingExpression = arguments.get( i );
final String subModelPartName;
if ( orderingExpression instanceof DomainPath ) {
final String partName = ( (DomainPath) orderingExpression ).getNavigablePath().getUnaliasedLocalName();
if ( CollectionPart.Nature.ELEMENT.getName().equals( partName ) ) {
subModelPartName = AbstractDomainPath.ELEMENT_TOKEN;
}
else {
subModelPartName = partName;
}
}
else {
subModelPartName = null;
}
args.add( orderingExpression.resolve( ast, tableGroup, subModelPartName, creationState ) );
}
return new SelfRenderingFunctionSqlAstExpression(
name,
this,
args,
null,
tableGroup.getModelPart().findSubPart( modelPartName, null )
);
}
@Override @Override
public void apply( public void apply(
QuerySpec ast, QuerySpec ast,
@ -52,6 +99,21 @@ public class FunctionExpression implements OrderingExpression {
String modelPartName, String modelPartName,
SortOrder sortOrder, SortOrder sortOrder,
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() ); final SelfRenderingFunctionSqlAstExpression expression = resolve( ast, tableGroup, modelPartName, creationState );
ast.addSortSpecification( new SortSpecification( expression, collation, sortOrder ) );
}
@Override
public void render(SqlAppender sqlAppender, List<SqlAstNode> sqlAstArguments, SqlAstTranslator<?> walker) {
sqlAppender.appendSql( name );
sqlAppender.appendSql( '(' );
if ( !sqlAstArguments.isEmpty() ) {
sqlAstArguments.get( 0 ).accept( walker );
for ( int i = 1; i < sqlAstArguments.size(); i++ ) {
sqlAppender.appendSql( ", " );
sqlAstArguments.get( i ).accept( walker );
}
}
sqlAppender.appendSql( ')' );
} }
} }

View File

@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping.ordering.ast;
import org.hibernate.query.SortOrder; import org.hibernate.query.SortOrder;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
@ -17,6 +18,9 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface OrderingExpression extends Node { public interface OrderingExpression extends Node {
SqlAstNode resolve(QuerySpec ast, TableGroup tableGroup, String modelPartName, SqlAstCreationState creationState);
/** /**
* Apply the SQL AST sort-specifications associated with this ordering-expression * Apply the SQL AST sort-specifications associated with this ordering-expression
*/ */

View File

@ -0,0 +1,77 @@
/*
* 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.procedure.internal;
import java.util.function.BiFunction;
import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultBuilderBasicValued;
import org.hibernate.query.results.complete.EntityResultImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderBasic;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.entity.EntityResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* @author Christian Beikov
*/
public class EntityDomainResultBuilder<T> implements ResultBuilder {
private final NavigablePath navigablePath;
private final EntityMappingType entityDescriptor;
private final ResultBuilderBasicValued discriminatorResultBuilder;
public EntityDomainResultBuilder(EntityMappingType entityDescriptor) {
this.entityDescriptor = entityDescriptor;
this.navigablePath = new NavigablePath( entityDescriptor.getEntityName() );
final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping();
if ( discriminatorMapping == null ) {
this.discriminatorResultBuilder = null;
}
else {
this.discriminatorResultBuilder = new ImplicitModelPartResultBuilderBasic(
navigablePath,
discriminatorMapping
);
}
}
@Override
public EntityResult buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
int resultPosition,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState domainResultCreationState) {
final BasicResult<?> discriminatorResult;
if ( discriminatorResultBuilder == null ) {
discriminatorResult = null;
}
else {
discriminatorResult = discriminatorResultBuilder.buildResult(
jdbcResultsMetadata,
resultPosition,
legacyFetchResolver,
domainResultCreationState
);
}
return new EntityResultImpl(
navigablePath,
entityDescriptor,
null,
LockMode.NONE,
discriminatorResult,
domainResultCreationState
);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.procedure.internal;
import java.util.function.BiFunction;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class ScalarDomainResultBuilder<T> implements ResultBuilder {
private final JavaTypeDescriptor<T> typeDescriptor;
public ScalarDomainResultBuilder(JavaTypeDescriptor<T> typeDescriptor) {
this.typeDescriptor = typeDescriptor;
}
@Override
public DomainResult<T> buildResult(
JdbcValuesMetadata jdbcResultsMetadata,
int resultPosition,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState domainResultCreationState) {
return new BasicResult<>( resultPosition, null, typeDescriptor );
}
}

View File

@ -1,33 +0,0 @@
/*
* 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.procedure.internal;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
/**
* @author Steve Ebersole
*/
public class ScalarDomainResultProducer<T> implements DomainResultProducer<T> {
private final SqmExpressable<T> expressableType;
public ScalarDomainResultProducer(SqmExpressable<T> expressableType) {
this.expressableType = expressableType;
}
@Override
public DomainResult<T> createDomainResult(
String resultVariable,
DomainResultCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() );
// //noinspection unchecked
// return new ScalarDomainResultImpl( resultVariable, expressableType.getExpressableJavaTypeDescriptor() );
}
}

View File

@ -10,10 +10,15 @@ import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.internal.ResultSetMappingResolutionContext; import org.hibernate.query.internal.ResultSetMappingResolutionContext;
import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.results.ResultSetMapping;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -70,26 +75,23 @@ public class Util {
ResultSetMapping resultSetMapping, ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer, Consumer<String> querySpaceConsumer,
ResultSetMappingResolutionContext context) { ResultSetMappingResolutionContext context) {
throw new NotYetImplementedFor6Exception( Util.class ); final MappingMetamodel domainModel = context.getSessionFactory().getDomainModel();
final TypeConfiguration typeConfiguration = domainModel.getTypeConfiguration();
// final DomainMetamodel domainModel = sessionFactory.getDomainModel(); for ( Class<?> resultSetMappingClass : resultSetMappingClasses ) {
// final TypeConfiguration typeConfiguration = domainModel.getTypeConfiguration(); final JavaTypeDescriptor<?> basicType = typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( resultSetMappingClass );
// if ( basicType != null ) {
// for ( Class resultSetMappingClass : resultSetMappingClasses ) { resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( basicType ) );
// final BasicType basicType = typeConfiguration.getBasicTypeForJavaType( resultSetMappingClass ); continue;
// if ( basicType != null ) { }
// //noinspection unchecked
// resultProducerConsumer.accept( new ScalarDomainResultProducer<>( basicType ) ); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( resultSetMappingClass );
// continue; if ( entityDescriptor != null ) {
// } resultSetMapping.addResultBuilder( new EntityDomainResultBuilder( entityDescriptor ) );
// for ( String querySpace : entityDescriptor.getSynchronizedQuerySpaces() ) {
// final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( resultSetMappingClass ); querySpaceConsumer.accept( querySpace );
// if ( entityDescriptor != null ) { }
// resultProducerConsumer.accept( new Entity ); }
// for ( String querySpace : entityDescriptor.getSynchronizedQuerySpaces() ) { }
// querySpaceConsumer.accept( querySpace );
// }
// }
// }
} }
} }

View File

@ -18,7 +18,9 @@ import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.internal.ResultSetMappingResolutionContext; import org.hibernate.query.internal.ResultSetMappingResolutionContext;
@ -33,6 +35,8 @@ import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.query.results.implicit.ImplicitFetchBuilder; import org.hibernate.query.results.implicit.ImplicitFetchBuilder;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderBasic; import org.hibernate.query.results.implicit.ImplicitFetchBuilderBasic;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEmbeddable; import org.hibernate.query.results.implicit.ImplicitFetchBuilderEmbeddable;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEntity;
import org.hibernate.query.results.implicit.ImplicitFetchBuilderPlural;
import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity; import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
@ -258,9 +262,13 @@ public class Builders {
return new ImplicitFetchBuilderEmbeddable( fetchPath, embeddableValuedFetchable, creationState ); return new ImplicitFetchBuilderEmbeddable( fetchPath, embeddableValuedFetchable, creationState );
} }
if ( fetchable instanceof EntityValuedFetchable ) { if ( fetchable instanceof ToOneAttributeMapping ) {
final EntityValuedFetchable entityValuedFetchable = (EntityValuedFetchable) fetchable; final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) fetchable;
throw new NotYetImplementedFor6Exception( "Support for implicit entity-valued fetches is not yet implemented" ); return new ImplicitFetchBuilderEntity( fetchPath, toOneAttributeMapping, creationState );
}
if ( fetchable instanceof PluralAttributeMapping ) {
return new ImplicitFetchBuilderPlural( fetchPath, (PluralAttributeMapping) fetchable, creationState );
} }
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -14,13 +14,17 @@ import java.util.function.Function;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl; import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
import org.hibernate.query.EntityIdentifierNavigablePath; import org.hibernate.query.EntityIdentifierNavigablePath;
@ -43,8 +47,6 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer; import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -317,8 +319,20 @@ public class DomainResultCreationStateImpl
final NavigablePath relativePath = relativePathStack.isEmpty() final NavigablePath relativePath = relativePathStack.isEmpty()
? new NavigablePath( fetchableName ) ? new NavigablePath( fetchableName )
: relativePathStack.getCurrent().append( fetchableName ); : relativePathStack.getCurrent().append( fetchableName );
// todo (6.0): figure out if we can somehow create the navigable paths in a better way
relativePathStack.push( relativePath ); if ( fetchable instanceof Association && fetchable.getMappedFetchOptions().getTiming() == FetchTiming.DELAYED ) {
final Association association = (Association) fetchable;
final ForeignKeyDescriptor foreignKeyDescriptor = association.getForeignKeyDescriptor();
if ( foreignKeyDescriptor.getPartMappingType() instanceof EmbeddableMappingType ) {
relativePathStack.push( relativePath.append( ( (EmbeddableMappingType) foreignKeyDescriptor.getPartMappingType() ).getPartName() ) );
}
else {
relativePathStack.push( relativePath.append( foreignKeyDescriptor.getPartName() ) );
}
}
else {
relativePathStack.push( relativePath );
}
try { try {
final FetchBuilder explicitFetchBuilder = fetchBuilderResolverStack final FetchBuilder explicitFetchBuilder = fetchBuilderResolverStack
.getCurrent() .getCurrent()

View File

@ -42,8 +42,8 @@ public class ImplicitAttributeFetchBuilder implements FetchBuilder, ImplicitFetc
DomainResultCreationState domainResultCreationState) { DomainResultCreationState domainResultCreationState) {
assert fetchPath.equals( navigablePath ); assert fetchPath.equals( navigablePath );
return attributeMapping.generateFetch( return parent.generateFetchableFetch(
parent, attributeMapping,
fetchPath, fetchPath,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
true, true,

View File

@ -104,8 +104,8 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Mode
processingState -> new SqlSelectionImpl( valuesArrayPosition, referencedModelPart ) processingState -> new SqlSelectionImpl( valuesArrayPosition, referencedModelPart )
); );
return referencedModelPart.generateFetch( return parent.generateFetchableFetch(
parent, referencedModelPart,
fetchPath, fetchPath,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
true, true,

View File

@ -99,8 +99,8 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
} }
); );
final Fetch fetch = fetchable.generateFetch( final Fetch fetch = parent.generateFetchableFetch(
parent, fetchable,
fetchPath, fetchPath,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
true, true,
@ -108,16 +108,16 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
null, null,
creationState creationState
); );
final FetchParent fetchParent = (FetchParent) fetch; // final FetchParent fetchParent = (FetchParent) fetch;
fetchBuilders.forEach( // fetchBuilders.forEach(
(subFetchPath, fetchBuilder) -> fetchBuilder.buildFetch( // (subFetchPath, fetchBuilder) -> fetchBuilder.buildFetch(
fetchParent, // fetchParent,
subFetchPath, // subFetchPath,
jdbcResultsMetadata, // jdbcResultsMetadata,
legacyFetchResolver, // legacyFetchResolver,
creationState // creationState
) // )
); // );
return fetch; return fetch;
} }

View File

@ -0,0 +1,132 @@
/*
* 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.query.results.implicit;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.Builders;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import static org.hibernate.query.results.ResultsHelper.impl;
/**
* @author Steve Ebersole
*/
public class ImplicitFetchBuilderEntity implements ImplicitFetchBuilder {
private final NavigablePath fetchPath;
private final ToOneAttributeMapping fetchable;
private final Map<NavigablePath, FetchBuilder> fetchBuilders;
public ImplicitFetchBuilderEntity(
NavigablePath fetchPath,
ToOneAttributeMapping fetchable,
DomainResultCreationState creationState) {
this.fetchPath = fetchPath;
this.fetchable = fetchable;
final DomainResultCreationStateImpl creationStateImpl = impl( creationState );
final NavigablePath relativePath = creationStateImpl.getCurrentRelativePath();
final Function<String, FetchBuilder> fetchBuilderResolver = creationStateImpl.getCurrentExplicitFetchMementoResolver();
ForeignKeyDescriptor foreignKeyDescriptor = fetchable.getForeignKeyDescriptor();
final String associationKeyPropertyName;
if ( fetchable.getReferencedPropertyName() == null ) {
associationKeyPropertyName = fetchable.getEntityMappingType().getIdentifierMapping().getPartName();
}
else {
associationKeyPropertyName = fetchable.getReferencedPropertyName();
}
final NavigablePath associationKeyFetchPath = relativePath.append( associationKeyPropertyName );
final FetchBuilder explicitAssociationKeyFetchBuilder = fetchBuilderResolver
.apply( associationKeyFetchPath.getFullPath() );
final Map<NavigablePath, FetchBuilder> fetchBuilders;
if ( explicitAssociationKeyFetchBuilder == null ) {
final MappingType partMappingType = foreignKeyDescriptor.getPartMappingType();
if ( partMappingType instanceof EmbeddableMappingType ) {
final EmbeddableMappingType embeddableValuedModelPart = (EmbeddableMappingType) partMappingType;
fetchBuilders = new LinkedHashMap<>( embeddableValuedModelPart.getNumberOfFetchables() );
embeddableValuedModelPart.visitFetchables(
subFetchable -> {
final NavigablePath subFetchPath = associationKeyFetchPath.append( subFetchable.getFetchableName() );
final FetchBuilder explicitFetchBuilder = fetchBuilderResolver
.apply( subFetchPath.getFullPath() );
if ( explicitFetchBuilder == null ) {
fetchBuilders.put(
subFetchPath,
Builders.implicitFetchBuilder( fetchPath, subFetchable, creationStateImpl )
);
}
else {
fetchBuilders.put( subFetchPath, explicitFetchBuilder );
}
},
null
);
}
else {
fetchBuilders = Collections.emptyMap();
}
}
else {
fetchBuilders = Collections.singletonMap( associationKeyFetchPath, explicitAssociationKeyFetchBuilder );
}
this.fetchBuilders = fetchBuilders;
}
@Override
public Fetch buildFetch(
FetchParent parent,
NavigablePath fetchPath,
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState creationState) {
final Fetch fetch = parent.generateFetchableFetch(
fetchable,
fetchPath,
FetchTiming.DELAYED,
false,
LockMode.READ,
null,
creationState
);
// final FetchParent fetchParent = (FetchParent) fetch;
// fetchBuilders.forEach(
// (subFetchPath, fetchBuilder) -> fetchBuilder.buildFetch(
// fetchParent,
// subFetchPath,
// jdbcResultsMetadata,
// legacyFetchResolver,
// creationState
// )
// );
return fetch;
}
@Override
public String toString() {
return "ImplicitFetchBuilderEntity(" + fetchPath + ")";
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.query.results.implicit;
import java.util.function.BiFunction;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import static org.hibernate.query.results.ResultsHelper.impl;
/**
* @author Christian Beikov
*/
public class ImplicitFetchBuilderPlural implements ImplicitFetchBuilder {
private final NavigablePath fetchPath;
private final PluralAttributeMapping fetchable;
public ImplicitFetchBuilderPlural(
NavigablePath fetchPath,
PluralAttributeMapping fetchable,
DomainResultCreationState creationState) {
this.fetchPath = fetchPath;
this.fetchable = fetchable;
}
@Override
public Fetch buildFetch(
FetchParent parent,
NavigablePath fetchPath,
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState creationState) {
final DomainResultCreationStateImpl creationStateImpl = impl( creationState );
final Fetch fetch = parent.generateFetchableFetch(
fetchable,
fetchPath,
FetchTiming.DELAYED,
false,
LockMode.READ,
null,
creationState
);
return fetch;
}
@Override
public String toString() {
return "ImplicitFetchBuilderPlural(" + fetchPath + ")";
}
}

View File

@ -1558,45 +1558,49 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
); );
} }
if ( domainResults != null ) { final Stack<SqlAstProcessingState> processingStateStack = getProcessingStateStack();
final Stack<SqlAstProcessingState> processingStateStack = getProcessingStateStack(); final boolean needsDomainResults = domainResults != null && currentClauseContributesToTopLevelSelectClause();
final boolean collectDomainResults; final boolean collectDomainResults;
if ( processingStateStack.depth() == 1) { if ( processingStateStack.depth() == 1) {
collectDomainResults = true; collectDomainResults = needsDomainResults;
} }
else { else {
final SqlAstProcessingState current = processingStateStack.getCurrent(); final SqlAstProcessingState current = processingStateStack.getCurrent();
// Since we only want to create domain results for the first/left-most query spec within query groups, // Since we only want to create domain results for the first/left-most query spec within query groups,
// we have to check if the current query spec is the left-most. // we have to check if the current query spec is the left-most.
// This is the case when all upper level in-flight query groups are still empty // This is the case when all upper level in-flight query groups are still empty
collectDomainResults = processingStateStack.findCurrentFirst( collectDomainResults = needsDomainResults && processingStateStack.findCurrentFirst(
processingState -> { processingState -> {
if ( !( processingState instanceof SqlAstQueryPartProcessingState ) ) { if ( !( processingState instanceof SqlAstQueryPartProcessingState ) ) {
return Boolean.FALSE;
}
if ( processingState == current ) {
return null;
}
final QueryPart part = ( (SqlAstQueryPartProcessingState) processingState ).getInflightQueryPart();
if ( part instanceof QueryGroup ) {
if ( ( (QueryGroup) part ).getQueryParts().isEmpty() ) {
return null;
}
}
return Boolean.FALSE; return Boolean.FALSE;
} }
) == null; if ( processingState == current ) {
} return null;
if ( collectDomainResults ) { }
resultProducers.forEach( (alias, r) -> domainResults.add( r.createDomainResult( alias, this ) ) ); final QueryPart part = ( (SqlAstQueryPartProcessingState) processingState ).getInflightQueryPart();
} if ( part instanceof QueryGroup ) {
else { if ( ( (QueryGroup) part ).getQueryParts().isEmpty() ) {
resultProducers.forEach( (alias, r) -> r.applySqlSelections( this ) ); return null;
} }
}
return Boolean.FALSE;
}
) == null;
}
if ( collectDomainResults ) {
resultProducers.forEach( (alias, r) -> domainResults.add( r.createDomainResult( alias, this ) ) );
}
else {
resultProducers.forEach( (alias, r) -> r.applySqlSelections( this ) );
} }
return null; return null;
} }
private boolean currentClauseContributesToTopLevelSelectClause() {
// The current clause contributes to the top level select if the clause stack contains just SELECT
return currentClauseStack.findCurrentFirst( clause -> clause == Clause.SELECT ? null : clause ) == null;
}
protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) { protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) {
if ( groupByClauseExpression instanceof SqmAliasedNodeRef ) { if ( groupByClauseExpression instanceof SqmAliasedNodeRef ) {
final int aliasedNodeOrdinal = ( (SqmAliasedNodeRef) groupByClauseExpression ).getPosition(); final int aliasedNodeOrdinal = ( (SqmAliasedNodeRef) groupByClauseExpression ).getPosition();
@ -4303,8 +4307,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public InSubQueryPredicate visitInSubQueryPredicate(SqmInSubQueryPredicate predicate) { public InSubQueryPredicate visitInSubQueryPredicate(SqmInSubQueryPredicate predicate) {
return new InSubQueryPredicate( return new InSubQueryPredicate(
(Expression) predicate.getTestExpression().accept( this ), visitWithInferredType( predicate.getTestExpression(), predicate.getSubQueryExpression() ),
(QueryPart) predicate.getSubQueryExpression().accept( this ), visitWithInferredType( predicate.getSubQueryExpression(), predicate.getTestExpression() ),
predicate.isNegated() predicate.isNegated()
); );
} }
@ -4515,8 +4519,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
try { try {
final Fetch fetch = fetchable.generateFetch( final Fetch fetch = fetchParent.generateFetchableFetch(
fetchParent, fetchable,
fetchablePath, fetchablePath,
fetchTiming, fetchTiming,
joined, joined,

View File

@ -45,7 +45,7 @@ public abstract class AbstractSqmPathInterpretation<T> implements SqmPathInterpr
return mapping; return mapping;
} }
protected TableGroup getTableGroup() { public TableGroup getTableGroup() {
return tableGroup; return tableGroup;
} }

View File

@ -35,7 +35,7 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
SqmBasicValuedSimplePath<T> sqmPath, SqmBasicValuedSimplePath<T> sqmPath,
SqlAstCreationState sqlAstCreationState, SqlAstCreationState sqlAstCreationState,
SemanticQueryWalker sqmWalker) { SemanticQueryWalker sqmWalker) {
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( sqmPath.getLhs().getNavigablePath() ); TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( sqmPath.getLhs().getNavigablePath() );
final BasicValuedModelPart mapping = (BasicValuedModelPart) tableGroup.getModelPart().findSubPart( final BasicValuedModelPart mapping = (BasicValuedModelPart) tableGroup.getModelPart().findSubPart(
sqmPath.getReferencedPathSource().getPathName(), sqmPath.getReferencedPathSource().getPathName(),

View File

@ -34,7 +34,7 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
SqmEmbeddedValuedSimplePath<T> sqmPath, SqmEmbeddedValuedSimplePath<T> sqmPath,
SqmToSqlAstConverter converter, SqmToSqlAstConverter converter,
SemanticQueryWalker sqmWalker) { SemanticQueryWalker sqmWalker) {
final TableGroup tableGroup = converter.getFromClauseAccess() TableGroup tableGroup = converter.getFromClauseAccess()
.findTableGroup( sqmPath.getLhs().getNavigablePath() ); .findTableGroup( sqmPath.getLhs().getNavigablePath() );
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) tableGroup.getModelPart().findSubPart( final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) tableGroup.getModelPart().findSubPart(

View File

@ -17,7 +17,6 @@ import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
@ -153,6 +152,14 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
? (SqlTuple) sqlExpression ? (SqlTuple) sqlExpression
: null; : null;
} }
@Override
public void applySqlSelections(DomainResultCreationState creationState) {
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
sqlExpression,
getExpressionType().getJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
}
@Override @Override
public EntityValuedModelPart getExpressionType() { public EntityValuedModelPart getExpressionType() {

View File

@ -8,6 +8,8 @@ package org.hibernate.sql.results.graph;
import java.util.List; import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.Association; import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -64,4 +66,23 @@ public interface FetchParent extends DomainResultGraphNode {
List<Fetch> getFetches(); List<Fetch> getFetches();
Fetch findFetch(Fetchable fetchable); Fetch findFetch(Fetchable fetchable);
default Fetch generateFetchableFetch(
Fetchable fetchable,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
return fetchable.generateFetch(
this,
fetchablePath,
fetchTiming,
selected,
lockMode,
resultVariable,
creationState
);
}
} }

View File

@ -6,18 +6,12 @@
*/ */
package org.hibernate.sql.results.graph.embeddable.internal; package org.hibernate.sql.results.graph.embeddable.internal;
import java.util.ArrayList; import org.hibernate.LockMode;
import java.util.List;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.internal.util.MutableInteger;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.AbstractFetchParent; import org.hibernate.sql.results.graph.AbstractFetchParent;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -25,12 +19,8 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode; import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl;
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
/** /**
* @author Andrea Boriero * @author Andrea Boriero
@ -43,22 +33,13 @@ public class EmbeddableForeignKeyResultImpl<T>
private final String resultVariable; private final String resultVariable;
public EmbeddableForeignKeyResultImpl( public EmbeddableForeignKeyResultImpl(
List<SqlSelection> sqlSelections,
NavigablePath navigablePath, NavigablePath navigablePath,
EmbeddableValuedModelPart embeddableValuedModelPart, EmbeddableValuedModelPart embeddableValuedModelPart,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
super( embeddableValuedModelPart.getEmbeddableTypeDescriptor(), navigablePath.append( ROLE_LOCAL_NAME ) ); super( embeddableValuedModelPart.getEmbeddableTypeDescriptor(), navigablePath.append( ROLE_LOCAL_NAME ) );
this.resultVariable = resultVariable; this.resultVariable = resultVariable;
fetches = new ArrayList<>(); this.fetches = creationState.visitFetches( this );
MutableInteger index = new MutableInteger();
embeddableValuedModelPart.visitFetchables(
fetchable ->
generateFetches( sqlSelections, navigablePath, creationState, index, fetchable )
,
null
);
} }
@Override @Override
@ -66,62 +47,32 @@ public class EmbeddableForeignKeyResultImpl<T>
return true; return true;
} }
private void generateFetches(
List<SqlSelection> sqlSelections,
NavigablePath navigablePath,
DomainResultCreationState creationState,
MutableInteger mutableInteger,
Fetchable fetchable) {
if ( fetchable instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) fetchable;
EntityMappingType associatedEntityMappingType = toOneAttributeMapping.getAssociatedEntityMappingType();
BasicResult domainResult = new BasicResult(
sqlSelections.get( mutableInteger.getAndIncrement() ).getValuesArrayPosition(),
null,
associatedEntityMappingType.getIdentifierMapping().getJavaTypeDescriptor()
);
Fetch fetch;
if ( toOneAttributeMapping.getMappedFetchOptions().getTiming() == FetchTiming.DELAYED ) {
fetch = new EntityDelayedFetchImpl(
this,
toOneAttributeMapping,
navigablePath.append( fetchable.getFetchableName() ),
domainResult
);
}
else {
fetch = new EntityFetchSelectImpl(
this,
toOneAttributeMapping,
false,
navigablePath.append( fetchable.getFetchableName() ),
domainResult,
false,
creationState
);
}
fetches.add( fetch );
}
else {
final Fetch fetch = new BasicFetch(
sqlSelections.get( mutableInteger.getAndIncrement() ).getValuesArrayPosition(),
null,
navigablePath.append( fetchable.getFetchableName() ),
(BasicValuedModelPart) fetchable,
true,
null,
FetchTiming.IMMEDIATE,
creationState
);
fetches.add( fetch );
}
}
@Override @Override
public String getResultVariable() { public String getResultVariable() {
return resultVariable; return resultVariable;
} }
@Override
public Fetch generateFetchableFetch(
Fetchable fetchable,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
return fetchable.generateFetch(
this,
fetchablePath,
fetchTiming,
// We need to make sure to-ones are always delayed to avoid cycles while resolving entity keys
selected && !( fetchable instanceof ToOneAttributeMapping ),
lockMode,
resultVariable,
creationState
);
}
@Override @Override
public DomainResultAssembler<T> createResultAssembler(AssemblerCreationState creationState) { public DomainResultAssembler<T> createResultAssembler(AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer(

View File

@ -205,8 +205,8 @@ public class NonRootTablePolymorphicTests {
assertThat( childFk.getTargetTable(), is( "sub_child" ) ); assertThat( childFk.getTargetTable(), is( "sub_child" ) );
assertThat( childFk.getJdbcTypeCount(), is( 1 ) ); assertThat( childFk.getJdbcTypeCount(), is( 1 ) );
assertThat( childFk.getKeySide().getSelectableMapping().getSelectionExpression(), is( "child_fk" ) ); assertThat( childFk.getKeySide().getSelectionExpression(), is( "child_fk" ) );
assertThat( childFk.getTargetSide().getSelectableMapping().getSelectionExpression(), is( "child_id" ) ); assertThat( childFk.getTargetSide().getSelectionExpression(), is( "child_id" ) );
// check Parent#sub fk // check Parent#sub fk
@ -218,8 +218,8 @@ public class NonRootTablePolymorphicTests {
assertThat( subFk.getTargetTable(), is( "sub" ) ); assertThat( subFk.getTargetTable(), is( "sub" ) );
assertThat( subFk.getJdbcTypeCount(), is( 1 ) ); assertThat( subFk.getJdbcTypeCount(), is( 1 ) );
assertThat( subFk.getKeySide().getSelectableMapping().getSelectionExpression(), is( "parent_sub_fk" ) ); assertThat( subFk.getKeySide().getSelectionExpression(), is( "parent_sub_fk" ) );
assertThat( subFk.getTargetSide().getSelectableMapping().getSelectionExpression(), is( "sub_id" ) ); assertThat( subFk.getTargetSide().getSelectionExpression(), is( "sub_id" ) );
scope.inTransaction( scope.inTransaction(
(session) -> { (session) -> {