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",
write = "? * 100"
)
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
private MonetaryAmount wallet;
//Getters and setters omitted for brevity

View File

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

View File

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

View File

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

View File

@ -18,17 +18,22 @@ import javax.persistence.FieldResult;
import javax.persistence.SqlResultSetMapping;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.internal.FetchMementoBasicStandard;
import org.hibernate.query.internal.ImplicitAttributeFetchMemento;
import org.hibernate.query.internal.ModelPartResultMementoBasicImpl;
import org.hibernate.query.internal.NamedResultSetMappingMementoImpl;
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.ResultMementoBasic;
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;
/**
@ -330,36 +336,32 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
private final NavigablePath navigablePath;
private final String entityName;
private final String relativeFetchPath;
private final String propertyPath;
private final String[] propertyPathParts;
private final List<String> columnNames;
private AttributeFetchDescriptor(
NavigablePath entityPath,
String entityName,
String relativeFetchPath,
String propertyPath,
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.relativeFetchPath = relativeFetchPath;
this.propertyPath = propertyPath;
this.propertyPathParts = propertyPath.split( "\\." );
this.navigablePath = entityPath;
this.columnNames = new ArrayList<>();
columnNames.add( columnName );
}
private void addColumn(FieldResult fieldResult) {
if ( ! relativeFetchPath.equals( fieldResult.name() ) ) {
if ( ! propertyPath.equals( fieldResult.name() ) ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Passed FieldResult [%s, %s] does not match AttributeFetchMapping [%s]",
fieldResult.name(),
fieldResult.column(),
relativeFetchPath
propertyPath
)
);
}
@ -374,7 +376,7 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels();
final EntityMappingType entityMapping = runtimeMetamodels.getEntityMappingType( entityName );
final ModelPart subPart = entityMapping.findSubPart( relativeFetchPath, null );
final ModelPart subPart = entityMapping.findSubPart( propertyPath, null );
//noinspection StatementWithEmptyBody
if ( subPart == null ) {
@ -389,7 +391,7 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
}
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 + "]"
);
}
@ -399,15 +401,34 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels();
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 ) {
assert columnNames.size() == 1;
final BasicValuedModelPart basicPart = (BasicValuedModelPart) subPart;
return new FetchMementoBasicStandard( navigablePath, basicPart, columnNames.get( 0 ) );
}
else if ( subPart instanceof EntityValuedModelPart ) {
return new ImplicitAttributeFetchMemento( navigablePath, (AttributeMapping) subPart );
}
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 + "]"
);
}

View File

@ -23,6 +23,7 @@ import org.hibernate.annotations.CacheModeType;
import org.hibernate.annotations.FlushModeType;
import org.hibernate.annotations.QueryHints;
import org.hibernate.boot.internal.NamedHqlQueryDefinitionImpl;
import org.hibernate.boot.internal.NamedProcedureCallDefinitionImpl;
import org.hibernate.boot.query.NamedNativeQueryDefinitionBuilder;
import org.hibernate.boot.spi.MetadataBuildingContext;
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 NotYetImplementedFor6Exception();
// NamedProcedureCallDefinition.
// final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation );
//
// if (isDefault) {
// context.getMetadataCollector().addDefaultNamedProcedureCall( def );
// }
// else {
// context.getMetadataCollector().addNamedProcedureCallDefinition( def );
// }
// LOG.debugf( "Bound named stored procedure query : %s => %s", def.getRegistrationName(), def.getProcedureName() );
final NamedProcedureCallDefinitionImpl def = new NamedProcedureCallDefinitionImpl( annotation );
if ( isDefault ) {
context.getMetadataCollector().addDefaultNamedProcedureCall( def );
}
else {
context.getMetadataCollector().addNamedProcedureCallDefinition( def );
}
LOG.debugf( "Bound named stored procedure query : %s => %s", def.getRegistrationName(), def.getProcedureName() );
}
public static void bindSqlResultSetMappings(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,8 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.config.spi.ConfigurationService;
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.Selectable;
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.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
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.spi.EmbeddableRepresentationStrategy;
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(
Component bootDescriptor,
CompositeType compositeType,

View File

@ -6,7 +6,12 @@
*/
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.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -27,6 +32,8 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
String getTargetTable();
ModelPart getKeyPart();
DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath,
TableGroup tableGroup,
@ -84,5 +91,12 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
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();
}

View File

@ -6,6 +6,9 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.query.SortOrder;
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.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.ColumnReference;
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.TableReference;
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 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
public void apply(
QuerySpec ast,
@ -97,7 +173,7 @@ public abstract class AbstractDomainPath implements DomainPath {
}
else {
// 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.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
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
public JdbcMapping getJdbcMapping() {
return jdbcMapping;

View File

@ -16,11 +16,13 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
@ -99,6 +101,31 @@ public class EmbeddedAttributeMapping
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
public EmbeddableMappingType getMappedType() {
return getEmbeddableTypeDescriptor();

View File

@ -8,8 +8,11 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
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.mapping.IndexedConsumer;
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.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.model.domain.NavigableRole;
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.SqlAstJoinType;
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.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
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.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
@ -47,7 +51,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
*/
public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
private final EmbeddableValuedModelPart mappingType;
private final EmbeddableValuedModelPart keyMappingType;
private final EmbeddableValuedModelPart targetMappingType;
private final String keyTable;
private final SelectableMappings keySelectableMappings;
private final String targetTable;
@ -55,7 +60,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
private AssociationKey associationKey;
public EmbeddedForeignKeyDescriptor(
EmbeddableValuedModelPart mappingType,
EmbeddableValuedModelPart targetMappingType,
String keyTable,
SelectableMappings keySelectableMappings,
String targetTable,
@ -65,24 +70,45 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
this.keySelectableMappings = keySelectableMappings;
this.targetTable = targetTable;
this.targetSelectableMappings = targetSelectableMappings;
this.mappingType = mappingType;
this.targetMappingType = targetMappingType;
this.keyMappingType = EmbeddedAttributeMapping.createInverseModelPart(
targetMappingType,
keySelectableMappings,
creationProcess
);
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?
// - 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() ) {
// todo (6.0) : ^^ for now, this is the only way we "know" that the embeddable has not been finalized yet
return false;
}
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
public String getKeyTable() {
return keyTable;
@ -93,6 +119,22 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
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
public DomainResult createCollectionFetchDomainResult(
NavigablePath collectionPath,
@ -103,7 +145,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
collectionPath,
tableGroup,
targetTable,
targetSelectableMappings,
targetMappingType,
creationState
);
}
@ -112,7 +154,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
collectionPath,
tableGroup,
keyTable,
keySelectableMappings,
keyMappingType,
creationState
);
}
@ -120,84 +162,70 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override
public DomainResult createDomainResult(
NavigablePath collectionPath,
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
return createDomainResult(
collectionPath,
navigablePath,
tableGroup,
keyTable,
keySelectableMappings,
keyMappingType,
creationState
);
}
@Override
public DomainResult createDomainResult(
NavigablePath collectionPath,
NavigablePath navigablePath,
TableGroup tableGroup,
boolean isKeyReferringSide,
DomainResultCreationState creationState) {
if ( isKeyReferringSide ) {
return createDomainResult(
collectionPath,
navigablePath,
tableGroup,
keyTable,
keySelectableMappings,
keyMappingType,
creationState
);
}
else {
return createDomainResult(
collectionPath,
navigablePath,
tableGroup,
targetTable,
targetSelectableMappings,
targetMappingType,
creationState
);
}
}
private DomainResult createDomainResult(
NavigablePath collectionPath,
NavigablePath navigablePath,
TableGroup tableGroup,
String columnContainingTable,
SelectableMappings selectableMappings,
EmbeddableValuedModelPart modelPart,
DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( columnContainingTable );
final String identificationVariable = tableReference.getIdentificationVariable();
final List<SqlSelection> sqlSelections = new ArrayList<>( selectableMappings.getJdbcTypeCount() );
selectableMappings.forEachSelectable(
(columnIndex, selection) -> {
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selection.getSelectionExpression()
),
s ->
new ColumnReference(
identificationVariable,
selection,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
selection.getJdbcMapping().getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
final NavigablePath fkNavigablePath = navigablePath.append( getPartName() );
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
fkNavigablePath,
np -> {
final TableGroupJoin tableGroupJoin = modelPart.createTableGroupJoin(
fkNavigablePath,
tableGroup,
null,
SqlAstJoinType.INNER,
LockMode.NONE,
creationState.getSqlAstCreationState()
);
sqlSelections.add( sqlSelection );
return tableGroupJoin.getJoinedGroup();
}
);
return new EmbeddableForeignKeyResultImpl<>(
sqlSelections,
collectionPath,
mappingType,
navigablePath,
modelPart,
null,
creationState
);
@ -344,19 +372,24 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return associationKey;
}
@Override
public ModelPart getKeyPart() {
return keyMappingType.getEmbeddableTypeDescriptor().getEmbeddedValueMapping();
}
@Override
public MappingType getPartMappingType() {
throw new HibernateException( "Unexpected call to SimpleForeignKeyDescriptor#getPartMappingType" );
return targetMappingType.getPartMappingType();
}
@Override
public JavaTypeDescriptor<?> getJavaTypeDescriptor() {
return mappingType.getJavaTypeDescriptor();
return targetMappingType.getJavaTypeDescriptor();
}
@Override
public NavigableRole getNavigableRole() {
throw new UnsupportedOperationException();
return targetMappingType.getNavigableRole();
}
@Override
@ -365,41 +398,26 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
//noinspection unchecked
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( keyTable );
final String identificationVariable = tableReference.getIdentificationVariable();
final int size = keySelectableMappings.getJdbcTypeCount();
final List<SqlSelection> sqlSelections = new ArrayList<>( size );
keySelectableMappings.forEachSelectable(
(columnIndex, selection) -> {
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selection.getSelectionExpression()
),
s ->
new ColumnReference(
identificationVariable,
selection,
creationState.getSqlAstCreationState()
.getCreationContext()
.getSessionFactory()
)
),
selection.getJdbcMapping().getJavaTypeDescriptor(),
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
final NavigablePath fkNavigablePath = navigablePath.append( getPartName() );
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
fkNavigablePath,
np -> {
final TableGroupJoin tableGroupJoin = keyMappingType.createTableGroupJoin(
fkNavigablePath,
tableGroup,
null,
null,
LockMode.NONE,
creationState.getSqlAstCreationState()
);
sqlSelections.add( sqlSelection );
return tableGroupJoin.getJoinedGroup();
}
);
return new EmbeddableForeignKeyResultImpl<>(
sqlSelections,
navigablePath,
mappingType,
keyMappingType,
resultVariable,
creationState
);
@ -419,8 +437,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override
public Object getAssociationKeyFromTarget(Object targetObject, SharedSessionContractImplementor session) {
// If the mapping type has an identifier type, that identifier is the key
if ( mappingType instanceof SingleAttributeIdentifierMapping ) {
return ( (SingleAttributeIdentifierMapping) mappingType ).getIdentifier( targetObject, session );
if ( targetMappingType instanceof SingleAttributeIdentifierMapping ) {
return ( (SingleAttributeIdentifierMapping) targetMappingType ).getIdentifier( targetObject, session );
}
// Otherwise this is a key based on the target object i.e. without id-class
return targetObject;
@ -428,12 +446,12 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override
public EntityMappingType findContainingEntityMapping() {
throw new UnsupportedOperationException();
return targetMappingType.findContainingEntityMapping();
}
@Override
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
return mappingType.forEachJdbcType( offset, action );
return targetMappingType.forEachJdbcType( offset, action );
}
@Override
@ -443,11 +461,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
int offset,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
return mappingType.forEachDisassembledJdbcValue( value, clause, offset, valuesConsumer, session );
return targetMappingType.forEachDisassembledJdbcValue( value, clause, offset, valuesConsumer, session );
}
@Override
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.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
@ -17,17 +19,16 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.ValueMapping;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.ComparisonOperator;
@ -57,8 +58,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Steve Ebersole
*/
public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicValuedModelPart, FetchOptions {
private final SideModelPart keySide;
private final SideModelPart targetSide;
private final BasicValuedModelPart keySide;
private final BasicValuedModelPart targetSide;
private final boolean refersToPrimaryKey;
@ -68,45 +69,57 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
public SimpleForeignKeyDescriptor(
SelectableMapping keySelectableMapping,
SelectableMapping targetSelectableMapping,
Function<Object,Object> disassemblyValueExtractor,
BasicValuedModelPart targetModelPart,
Function<Object, Object> disassemblyValueExtractor,
boolean refersToPrimaryKey) {
assert keySelectableMapping != null;
assert targetSelectableMapping != null;
assert targetModelPart != null;
assert disassemblyValueExtractor != null;
this.keySide = new SideModelPart( keySelectableMapping );
this.targetSide = new SideModelPart( targetSelectableMapping );
this.keySide = BasicValuedSingularAttributeMapping.withSelectableMapping( targetModelPart, keySelectableMapping );
this.targetSide = targetModelPart;
this.disassemblyValueExtractor = disassemblyValueExtractor;
this.refersToPrimaryKey = refersToPrimaryKey;
}
@Override
public String getKeyTable() {
return keySide.selectableMapping.getContainingTableExpression();
return keySide.getContainingTableExpression();
}
@Override
public String getTargetTable() {
return targetSide.selectableMapping.getContainingTableExpression();
return targetSide.getContainingTableExpression();
}
public SideModelPart getKeySide() {
public BasicValuedModelPart getKeySide() {
return keySide;
}
public SideModelPart getTargetSide() {
public BasicValuedModelPart getTargetSide() {
return targetSide;
}
@Override
public ForeignKeyDescriptor withKeySelectionMapping(
IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess) {
return new SimpleForeignKeyDescriptor(
selectableMappingAccess.apply( 0 ),
targetSide,
disassemblyValueExtractor,
refersToPrimaryKey
);
}
@Override
public DomainResult<?> createCollectionFetchDomainResult(
NavigablePath collectionPath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
if ( targetSide.selectableMapping.getContainingTableExpression()
.equals( keySide.selectableMapping.getContainingTableExpression() ) ) {
return createDomainResult( tableGroup, targetSide.selectableMapping, creationState );
if ( targetSide.getContainingTableExpression()
.equals( keySide.getContainingTableExpression() ) ) {
return createDomainResult( collectionPath, tableGroup, targetSide, creationState );
}
return createDomainResult( collectionPath, tableGroup, creationState );
}
@ -116,7 +129,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
return createDomainResult( tableGroup, keySide.selectableMapping, creationState );
return createDomainResult( navigablePath, tableGroup, keySide, creationState );
}
@Override
@ -126,9 +139,9 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
boolean isKeyReferringSide,
DomainResultCreationState creationState) {
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
@ -137,10 +150,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
return createDomainResult( tableGroup, keySide.selectableMapping, creationState );
return createDomainResult( navigablePath, tableGroup, keySide, creationState );
}
private <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
SelectableMapping selectableMapping,
DomainResultCreationState creationState) {
@ -180,17 +194,17 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
if ( lhs.getTableReference( keySide.selectableMapping.getContainingTableExpression() ) != null ) {
if ( lhs.getTableReference( keySide.getContainingTableExpression() ) != null ) {
return new ComparisonPredicate(
new ColumnReference(
lhs,
keySide.selectableMapping,
keySide,
creationContext.getSessionFactory()
),
ComparisonOperator.EQUAL,
new ColumnReference(
rhs,
targetSide.selectableMapping,
targetSide,
creationContext.getSessionFactory()
)
);
@ -199,13 +213,13 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
return new ComparisonPredicate(
new ColumnReference(
lhs,
targetSide.selectableMapping,
targetSide,
creationContext.getSessionFactory()
),
ComparisonOperator.EQUAL,
new ColumnReference(
rhs,
keySide.selectableMapping,
keySide,
creationContext.getSessionFactory()
)
);
@ -221,22 +235,22 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstCreationContext creationContext) {
TableReference lhsTableReference;
TableReference rhsTableKeyReference;
if ( targetSide.selectableMapping.getContainingTableExpression().equals( keySide.selectableMapping.getContainingTableExpression() ) ) {
lhsTableReference = getTableReferenceWhenTargetEqualsKey( lhs, tableGroup, keySide.selectableMapping.getContainingTableExpression() );
if ( targetSide.getContainingTableExpression().equals( keySide.getContainingTableExpression() ) ) {
lhsTableReference = getTableReferenceWhenTargetEqualsKey( lhs, tableGroup, keySide.getContainingTableExpression() );
rhsTableKeyReference = getTableReference(
lhs,
tableGroup,
targetSide.selectableMapping.getContainingTableExpression()
targetSide.getContainingTableExpression()
);
}
else {
lhsTableReference = getTableReference( lhs, tableGroup, keySide.selectableMapping.getContainingTableExpression() );
lhsTableReference = getTableReference( lhs, tableGroup, keySide.getContainingTableExpression() );
rhsTableKeyReference = getTableReference(
lhs,
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 + "`" );
}
@Override
public ModelPart getKeyPart() {
return keySide;
}
@Override
public MappingType getPartMappingType() {
return targetSide.getMappedType();
}
@Override
public JavaTypeDescriptor<?> getJavaTypeDescriptor() {
return targetSide.getJdbcMapping().getJavaTypeDescriptor();
@ -289,12 +313,12 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override
public NavigableRole getNavigableRole() {
throw new UnsupportedOperationException();
return targetSide.getNavigableRole();
}
@Override
public EntityMappingType findContainingEntityMapping() {
throw new UnsupportedOperationException();
return targetSide.findContainingEntityMapping();
}
@Override
@ -326,26 +350,26 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
valueConsumer.consume( domainValue, keySide.selectableMapping );
valueConsumer.consume( domainValue, keySide );
}
@Override
public int visitKeySelectables(int offset, SelectableConsumer consumer) {
consumer.accept( offset, keySide.selectableMapping );
consumer.accept( offset, keySide );
return getJdbcTypeCount();
}
@Override
public int visitTargetSelectables(int offset, SelectableConsumer consumer) {
consumer.accept( offset, targetSide.selectableMapping );
consumer.accept( offset, targetSide );
return getJdbcTypeCount();
}
@Override
public AssociationKey getAssociationKey() {
if ( associationKey == null ) {
final List<String> associationKeyColumns = Collections.singletonList( keySide.selectableMapping.getSelectionExpression() );
associationKey = new AssociationKey( keySide.selectableMapping.getContainingTableExpression(), associationKeyColumns );
final List<String> associationKeyColumns = Collections.singletonList( keySide.getSelectionExpression() );
associationKey = new AssociationKey( keySide.getContainingTableExpression(), associationKeyColumns );
}
return associationKey;
}
@ -375,27 +399,27 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override
public String getContainingTableExpression() {
return keySide.selectableMapping.getContainingTableExpression();
return keySide.getContainingTableExpression();
}
@Override
public String getSelectionExpression() {
return keySide.selectableMapping.getSelectionExpression();
return keySide.getSelectionExpression();
}
@Override
public boolean isFormula() {
return keySide.selectableMapping.isFormula();
return keySide.isFormula();
}
@Override
public String getCustomReadExpression() {
return keySide.selectableMapping.getCustomReadExpression();
return keySide.getCustomReadExpression();
}
@Override
public String getCustomWriteExpression() {
return keySide.selectableMapping.getCustomWriteExpression();
return keySide.getCustomWriteExpression();
}
@Override
@ -444,70 +468,10 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
public String toString() {
return String.format(
"SimpleForeignKeyDescriptor : %s.%s -> %s.%s",
keySide.selectableMapping.getContainingTableExpression(),
keySide.selectableMapping.getSelectionExpression(),
targetSide.selectableMapping.getContainingTableExpression(),
targetSide.selectableMapping.getSelectionExpression()
keySide.getContainingTableExpression(),
keySide.getSelectionExpression(),
targetSide.getContainingTableExpression(),
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.ToOne;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
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.DomainResultCreationState;
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.embeddable.EmbeddableValuedFetchable;
import org.hibernate.sql.results.graph.entity.EntityFetch;
@ -84,7 +87,7 @@ public class ToOneAttributeMapping
private final String referencedPropertyName;
private final Cardinality cardinality;
private String bidirectionalAttributeName;
private final String bidirectionalAttributeName;
private ForeignKeyDescriptor foreignKeyDescriptor;
private String identifyingColumnsTableExpression;
@ -122,6 +125,7 @@ public class ToOneAttributeMapping
else {
cardinality = Cardinality.MANY_TO_ONE;
}
this.bidirectionalAttributeName = null;
}
else {
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.
*/
// 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(),
'.'
);
if ( bidirectionalAttributeName == null ) {
bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
bootValue.getReferencedPropertyName(),
'.'
);
}
else {
this.bidirectionalAttributeName = bidirectionalAttributeName;
}
}
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) {
isKeyReferringSide = foreignKeyDescriptor.getAssociationKey().getTable().equals( identifyingColumnsTableExpression );
assert identifyingColumnsTableExpression != null;
@ -197,6 +228,7 @@ public class ToOneAttributeMapping
identifyingColumnsTableExpression = tableExpression;
}
@Override
public ForeignKeyDescriptor getForeignKeyDescriptor() {
return this.foreignKeyDescriptor;
}
@ -205,6 +237,10 @@ public class ToOneAttributeMapping
return referencedPropertyName;
}
public Cardinality getCardinality() {
return cardinality;
}
@Override
public EntityMappingType getMappedType() {
return getEntityMappingType();
@ -230,6 +266,35 @@ public class ToOneAttributeMapping
if ( creationState.isAssociationKeyVisited( associationKey ) ) {
NavigablePath parentNavigablePath = fetchablePath.getParent();
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 );
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) {
@ -407,6 +472,9 @@ public class ToOneAttributeMapping
fetchParent.getNavigablePath()
);
final NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) {
if ( fetchParent instanceof EntityResultJoinedSubclassImpl &&
( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) {
@ -471,12 +539,11 @@ public class ToOneAttributeMapping
selectByUniqueKey = false;
}
else {
// case 1.1
keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState );
// case 1.1
selectByUniqueKey = true;
}
assert !selected;
if ( fetchTiming == FetchTiming.IMMEDIATE ) {
return new EntityFetchSelectImpl(
fetchParent,

View File

@ -15,6 +15,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.SortOrder;
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.TableReference;
@ -57,19 +58,17 @@ public class ColumnReference implements OrderingExpression, SequencePart {
}
@Override
public void apply(
public Expression resolve(
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
TableReference tableReference;
tableReference = getTableReference( tableGroup );
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
return sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
sqlAstProcessingState -> new org.hibernate.sql.ast.tree.expression.ColumnReference(
tableReference,
@ -83,6 +82,17 @@ public class ColumnReference implements OrderingExpression, SequencePart {
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
// SQL Server even reports a query error in this case
if ( ast.hasSortSpecifications() ) {

View File

@ -11,17 +11,28 @@ import java.util.Collections;
import java.util.List;
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.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.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.TableReference;
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
*
* @author Steve Ebersole
*/
public class FunctionExpression implements OrderingExpression {
public class FunctionExpression implements OrderingExpression, FunctionRenderingSupport {
private final String name;
private final List<OrderingExpression> arguments;
@ -44,6 +55,42 @@ public class FunctionExpression implements OrderingExpression {
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
public void apply(
QuerySpec ast,
@ -52,6 +99,21 @@ public class FunctionExpression implements OrderingExpression {
String modelPartName,
SortOrder sortOrder,
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.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.select.QuerySpec;
@ -17,6 +18,9 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
* @author Steve Ebersole
*/
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
*/

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.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.named.NamedObjectRepository;
import org.hibernate.query.named.NamedResultSetMappingMemento;
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;
@ -70,26 +75,23 @@ public class Util {
ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer,
ResultSetMappingResolutionContext context) {
throw new NotYetImplementedFor6Exception( Util.class );
final MappingMetamodel domainModel = context.getSessionFactory().getDomainModel();
final TypeConfiguration typeConfiguration = domainModel.getTypeConfiguration();
// final DomainMetamodel domainModel = sessionFactory.getDomainModel();
// final TypeConfiguration typeConfiguration = domainModel.getTypeConfiguration();
//
// for ( Class resultSetMappingClass : resultSetMappingClasses ) {
// final BasicType basicType = typeConfiguration.getBasicTypeForJavaType( resultSetMappingClass );
// if ( basicType != null ) {
// //noinspection unchecked
// resultProducerConsumer.accept( new ScalarDomainResultProducer<>( basicType ) );
// continue;
// }
//
// final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( resultSetMappingClass );
// if ( entityDescriptor != null ) {
// resultProducerConsumer.accept( new Entity );
// for ( String querySpace : entityDescriptor.getSynchronizedQuerySpaces() ) {
// querySpaceConsumer.accept( querySpace );
// }
// }
// }
for ( Class<?> resultSetMappingClass : resultSetMappingClasses ) {
final JavaTypeDescriptor<?> basicType = typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( resultSetMappingClass );
if ( basicType != null ) {
resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( basicType ) );
continue;
}
final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( resultSetMappingClass );
if ( entityDescriptor != null ) {
resultSetMapping.addResultBuilder( new EntityDomainResultBuilder( entityDescriptor ) );
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.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
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.ImplicitFetchBuilderBasic;
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.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable;
@ -258,9 +262,13 @@ public class Builders {
return new ImplicitFetchBuilderEmbeddable( fetchPath, embeddableValuedFetchable, creationState );
}
if ( fetchable instanceof EntityValuedFetchable ) {
final EntityValuedFetchable entityValuedFetchable = (EntityValuedFetchable) fetchable;
throw new NotYetImplementedFor6Exception( "Support for implicit entity-valued fetches is not yet implemented" );
if ( fetchable instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) fetchable;
return new ImplicitFetchBuilderEntity( fetchPath, toOneAttributeMapping, creationState );
}
if ( fetchable instanceof PluralAttributeMapping ) {
return new ImplicitFetchBuilderPlural( fetchPath, (PluralAttributeMapping) fetchable, creationState );
}
throw new UnsupportedOperationException();

View File

@ -14,13 +14,17 @@ import java.util.function.Function;
import org.hibernate.Internal;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
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.Fetchable;
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.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
@ -317,8 +319,20 @@ public class DomainResultCreationStateImpl
final NavigablePath relativePath = relativePathStack.isEmpty()
? new NavigablePath( fetchableName )
: relativePathStack.getCurrent().append( fetchableName );
relativePathStack.push( relativePath );
// todo (6.0): figure out if we can somehow create the navigable paths in a better way
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 {
final FetchBuilder explicitFetchBuilder = fetchBuilderResolverStack
.getCurrent()

View File

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

View File

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

View File

@ -99,8 +99,8 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
}
);
final Fetch fetch = fetchable.generateFetch(
parent,
final Fetch fetch = parent.generateFetchableFetch(
fetchable,
fetchPath,
FetchTiming.IMMEDIATE,
true,
@ -108,16 +108,16 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
null,
creationState
);
final FetchParent fetchParent = (FetchParent) fetch;
fetchBuilders.forEach(
(subFetchPath, fetchBuilder) -> fetchBuilder.buildFetch(
fetchParent,
subFetchPath,
jdbcResultsMetadata,
legacyFetchResolver,
creationState
)
);
// final FetchParent fetchParent = (FetchParent) fetch;
// fetchBuilders.forEach(
// (subFetchPath, fetchBuilder) -> fetchBuilder.buildFetch(
// fetchParent,
// subFetchPath,
// jdbcResultsMetadata,
// legacyFetchResolver,
// creationState
// )
// );
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 boolean collectDomainResults;
if ( processingStateStack.depth() == 1) {
collectDomainResults = true;
}
else {
final SqlAstProcessingState current = processingStateStack.getCurrent();
// 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.
// This is the case when all upper level in-flight query groups are still empty
collectDomainResults = processingStateStack.findCurrentFirst(
processingState -> {
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;
}
}
final Stack<SqlAstProcessingState> processingStateStack = getProcessingStateStack();
final boolean needsDomainResults = domainResults != null && currentClauseContributesToTopLevelSelectClause();
final boolean collectDomainResults;
if ( processingStateStack.depth() == 1) {
collectDomainResults = needsDomainResults;
}
else {
final SqlAstProcessingState current = processingStateStack.getCurrent();
// 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.
// This is the case when all upper level in-flight query groups are still empty
collectDomainResults = needsDomainResults && processingStateStack.findCurrentFirst(
processingState -> {
if ( !( processingState instanceof SqlAstQueryPartProcessingState ) ) {
return Boolean.FALSE;
}
) == null;
}
if ( collectDomainResults ) {
resultProducers.forEach( (alias, r) -> domainResults.add( r.createDomainResult( alias, this ) ) );
}
else {
resultProducers.forEach( (alias, r) -> r.applySqlSelections( this ) );
}
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;
}
) == null;
}
if ( collectDomainResults ) {
resultProducers.forEach( (alias, r) -> domainResults.add( r.createDomainResult( alias, this ) ) );
}
else {
resultProducers.forEach( (alias, r) -> r.applySqlSelections( this ) );
}
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) {
if ( groupByClauseExpression instanceof SqmAliasedNodeRef ) {
final int aliasedNodeOrdinal = ( (SqmAliasedNodeRef) groupByClauseExpression ).getPosition();
@ -4303,8 +4307,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public InSubQueryPredicate visitInSubQueryPredicate(SqmInSubQueryPredicate predicate) {
return new InSubQueryPredicate(
(Expression) predicate.getTestExpression().accept( this ),
(QueryPart) predicate.getSubQueryExpression().accept( this ),
visitWithInferredType( predicate.getTestExpression(), predicate.getSubQueryExpression() ),
visitWithInferredType( predicate.getSubQueryExpression(), predicate.getTestExpression() ),
predicate.isNegated()
);
}
@ -4515,8 +4519,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
try {
final Fetch fetch = fetchable.generateFetch(
fetchParent,
final Fetch fetch = fetchParent.generateFetchableFetch(
fetchable,
fetchablePath,
fetchTiming,
joined,

View File

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

View File

@ -35,7 +35,7 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
SqmBasicValuedSimplePath<T> sqmPath,
SqlAstCreationState sqlAstCreationState,
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(
sqmPath.getReferencedPathSource().getPathName(),

View File

@ -34,7 +34,7 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
SqmEmbeddedValuedSimplePath<T> sqmPath,
SqmToSqlAstConverter converter,
SemanticQueryWalker sqmWalker) {
final TableGroup tableGroup = converter.getFromClauseAccess()
TableGroup tableGroup = converter.getFromClauseAccess()
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
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.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
@ -153,6 +152,14 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
? (SqlTuple) sqlExpression
: null;
}
@Override
public void applySqlSelections(DomainResultCreationState creationState) {
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
sqlExpression,
getExpressionType().getJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
}
@Override
public EntityValuedModelPart getExpressionType() {

View File

@ -8,6 +8,8 @@ package org.hibernate.sql.results.graph;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -64,4 +66,23 @@ public interface FetchParent extends DomainResultGraphNode {
List<Fetch> getFetches();
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;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.LockMode;
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.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
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.AssemblerCreationState;
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.Fetch;
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.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl;
import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
/**
* @author Andrea Boriero
@ -43,22 +33,13 @@ public class EmbeddableForeignKeyResultImpl<T>
private final String resultVariable;
public EmbeddableForeignKeyResultImpl(
List<SqlSelection> sqlSelections,
NavigablePath navigablePath,
EmbeddableValuedModelPart embeddableValuedModelPart,
String resultVariable,
DomainResultCreationState creationState) {
super( embeddableValuedModelPart.getEmbeddableTypeDescriptor(), navigablePath.append( ROLE_LOCAL_NAME ) );
this.resultVariable = resultVariable;
fetches = new ArrayList<>();
MutableInteger index = new MutableInteger();
embeddableValuedModelPart.visitFetchables(
fetchable ->
generateFetches( sqlSelections, navigablePath, creationState, index, fetchable )
,
null
);
this.fetches = creationState.visitFetches( this );
}
@Override
@ -66,62 +47,32 @@ public class EmbeddableForeignKeyResultImpl<T>
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
public String getResultVariable() {
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
public DomainResultAssembler<T> createResultAssembler(AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer(

View File

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