From 5500985afa8d5c2dd6b9040549a4a3aab9440c2e Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 16 Sep 2019 13:09:43 -0500 Subject: [PATCH] initial working support for root entity loading via HQL --- design/todo-6.0.adoc | 4 + .../mapping/BasicValuedModelPart.java | 3 +- .../hibernate/metamodel/mapping/Bindable.java | 1 + .../metamodel/mapping/MappingType.java | 5 + .../metamodel/mapping/ModelPart.java | 2 + .../internal/AbstractAttributeMapping.java | 6 + .../BasicValuedSingularAttributeMapping.java | 60 ++++++-- .../internal/MappingModelCreationHelper.java | 22 ++- .../model/domain/PersistentAttribute.java | 3 +- .../domain/SingularPersistentAttribute.java | 3 +- .../domain/internal/AbstractAttribute.java | 10 +- .../entity/AbstractEntityPersister.java | 7 +- .../exec/internal/AbstractJdbcParameter.java | 1 + .../internal/domain/AbstractFetchParent.java | 6 + .../domain/BiDirectionalFetchImpl.java | 110 ++++++++++++++ .../domain/RootBiDirectionalFetchImpl.java | 138 ++++++++++++++++++ .../internal/domain/basic/BasicFetch.java | 89 +++++++++++ .../basic}/BasicResultAssembler.java | 2 +- .../basic/BasicResultImpl.java} | 12 +- .../entity/AbstractEntityMappingNode.java | 20 +-- .../sql/results/spi/BiDirectionalFetch.java | 58 ++++++++ .../results/spi/CircularFetchDetector.java | 80 ++++------ .../sql/results/spi/FetchParent.java | 6 + .../java/org/hibernate/type/BasicType.java | 5 + .../orm/test/sql/ast/SmokeTests.java | 14 +- 25 files changed, 559 insertions(+), 108 deletions(-) create mode 100644 design/todo-6.0.adoc create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/BiDirectionalFetchImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/RootBiDirectionalFetchImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicFetch.java rename hibernate-core/src/main/java/org/hibernate/sql/results/internal/{ => domain/basic}/BasicResultAssembler.java (97%) rename hibernate-core/src/main/java/org/hibernate/sql/results/internal/{ScalarDomainResultImpl.java => domain/basic/BasicResultImpl.java} (91%) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/spi/BiDirectionalFetch.java diff --git a/design/todo-6.0.adoc b/design/todo-6.0.adoc new file mode 100644 index 0000000000..2defd64e28 --- /dev/null +++ b/design/todo-6.0.adoc @@ -0,0 +1,4 @@ +== Todo tasks related to 6.0 development + +* Currently filtering tests to limit tests run during builds from Gradle. This is done in `gradle/java-module.gradle` via a test.include filter +* Tests for hibernate-orm-modules are run but failures are ignored \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicValuedModelPart.java index 146fdb8a44..d10aaf721b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicValuedModelPart.java @@ -7,11 +7,12 @@ package org.hibernate.metamodel.mapping; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; +import org.hibernate.sql.results.spi.Fetchable; /** * @author Steve Ebersole */ -public interface BasicValuedModelPart extends BasicValuedMapping, ModelPart { +public interface BasicValuedModelPart extends BasicValuedMapping, ModelPart, Fetchable { /** * The table expression (table name or subselect) that contains * the {@linkplain #getMappedColumnExpression mapped column} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Bindable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Bindable.java index 35cf75c1d4..66687af59a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Bindable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Bindable.java @@ -15,6 +15,7 @@ import java.util.function.Consumer; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.sql.ast.Clause; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; /** diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingType.java index 200aa25431..15276a2893 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingType.java @@ -15,4 +15,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; */ public interface MappingType extends ModelPart { JavaTypeDescriptor getMappedJavaTypeDescriptor(); + + @Override + default JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedJavaTypeDescriptor(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java index f640b794e4..a934501691 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java @@ -12,6 +12,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultCreationState; import org.hibernate.query.sqm.sql.internal.DomainResultProducer; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * Describes a mapping of related to any part of the app's domain model - e.g. @@ -25,6 +26,7 @@ import org.hibernate.query.sqm.sql.internal.DomainResultProducer; * @author Steve Ebersole */ public interface ModelPart extends MappingModelExpressable { + JavaTypeDescriptor getJavaTypeDescriptor(); /** * Create a DomainResult for a specific reference to this ModelPart. diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractAttributeMapping.java index 4c282b67a0..e3a1369ce0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractAttributeMapping.java @@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping.internal; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * @author Steve Ebersole @@ -36,6 +37,11 @@ public abstract class AbstractAttributeMapping implements AttributeMapping { return type; } + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); + } + @Override public ManagedMappingType getDeclaringType() { return declaringType; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java index 4516f4abb9..e8912d8bc5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java @@ -8,7 +8,9 @@ package org.hibernate.metamodel.mapping.internal; import java.util.function.Consumer; +import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.JdbcMapping; @@ -23,9 +25,12 @@ import org.hibernate.sql.ast.Clause; 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.results.internal.ScalarDomainResultImpl; +import org.hibernate.sql.results.internal.domain.basic.BasicFetch; +import org.hibernate.sql.results.internal.domain.basic.BasicResultImpl; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; import org.hibernate.type.BasicType; import org.hibernate.type.spi.TypeConfiguration; @@ -89,8 +94,21 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { + final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState ); + + //noinspection unchecked + return new BasicResultImpl( + sqlSelection.getValuesArrayPosition(), + resultVariable, + getMappedTypeDescriptor().getMappedJavaTypeDescriptor(), + valueConverter, + navigablePath + ); + } + + private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) { final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); - final SqlSelection sqlSelection = expressionResolver.resolveSqlSelection( + return expressionResolver.resolveSqlSelection( expressionResolver.resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( getContainingTableExpression(), @@ -106,15 +124,6 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu valueConverter == null ? getMappedTypeDescriptor().getMappedJavaTypeDescriptor() : valueConverter.getRelationalJavaDescriptor(), creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration() ); - - //noinspection unchecked - return new ScalarDomainResultImpl( - sqlSelection.getValuesArrayPosition(), - resultVariable, - getMappedTypeDescriptor().getMappedJavaTypeDescriptor(), - valueConverter, - navigablePath - ); } @Override @@ -143,6 +152,30 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu ); } + @Override + public Fetch generateFetch( + FetchParent fetchParent, + FetchTiming fetchTiming, + boolean selected, + LockMode lockMode, + String resultVariable, + DomainResultCreationState creationState) { + final TableGroup tableGroup = creationState.getSqlAstCreationState() + .getFromClauseAccess() + .findTableGroup( fetchParent.getNavigablePath() ); + final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState ); + + return new BasicFetch( + sqlSelection.getValuesArrayPosition(), + fetchParent, + this, + getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), + getConverter(), + fetchTiming, + creationState + ); + } + @Override public Object disassemble(Object value, SharedSessionContractImplementor session) { if ( valueConverter != null ) { @@ -168,9 +201,4 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu TypeConfiguration typeConfiguration) { action.accept( getJdbcMapping() ); } - - @Override - public int getStateArrayPosition() { - return 0; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index 26fe9da5e3..ddb0bd3a09 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -25,11 +25,12 @@ import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.results.internal.ScalarDomainResultImpl; +import org.hibernate.sql.results.internal.domain.basic.BasicResultImpl; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultCreationState; import org.hibernate.type.BasicType; import org.hibernate.type.CompositeType; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; /** @@ -88,6 +89,11 @@ public class MappingModelCreationHelper { valuesConsumer.consume( value, idType ); } + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); + } + @Override public DomainResult createDomainResult( NavigablePath navigablePath, @@ -113,7 +119,7 @@ public class MappingModelCreationHelper { ); //noinspection unchecked - return new ScalarDomainResultImpl( + return new BasicResultImpl( sqlSelection.getValuesArrayPosition(), resultVariable, entityPersister.getIdentifierMapping().getMappedTypeDescriptor().getMappedJavaTypeDescriptor() @@ -177,6 +183,11 @@ public class MappingModelCreationHelper { return ( (BasicValuedModelPart) entityPersister.getIdentifierType() ).getMappedTypeDescriptor(); } + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); + } + @Override public DomainResult createDomainResult( NavigablePath navigablePath, @@ -227,7 +238,12 @@ public class MappingModelCreationHelper { @Override public MappingType getMappedTypeDescriptor() { - return null; + return entityPersister; + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/PersistentAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/PersistentAttribute.java index f2c904c570..0dca52b856 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/PersistentAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/PersistentAttribute.java @@ -9,7 +9,6 @@ package org.hibernate.metamodel.model.domain; import javax.persistence.metamodel.Attribute; import org.hibernate.metamodel.AttributeClassification; -import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** @@ -17,7 +16,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; * * @author Steve Ebersole */ -public interface PersistentAttribute extends Attribute, ModelPart { +public interface PersistentAttribute extends Attribute { @Override ManagedDomainType getDeclaringType(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/SingularPersistentAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/SingularPersistentAttribute.java index bd9bb675ef..ce8f719577 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/SingularPersistentAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/SingularPersistentAttribute.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.model.domain; import javax.persistence.metamodel.SingularAttribute; -import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.sqm.SqmJoinable; import org.hibernate.query.sqm.SqmPathSource; @@ -18,7 +17,7 @@ import org.hibernate.query.sqm.SqmPathSource; * @author Steve Ebersole */ public interface SingularPersistentAttribute - extends SingularAttribute, PersistentAttribute, ModelPart, SqmPathSource, SqmJoinable { + extends SingularAttribute, PersistentAttribute, SqmPathSource, SqmJoinable { @Override SimpleDomainType getType(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java index 0588cc14fb..84bd94edbe 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java @@ -33,7 +33,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; public abstract class AbstractAttribute implements PersistentAttribute, Serializable { private final ManagedDomainType declaringType; private final String name; - private final JavaTypeDescriptor attributeType; + private final JavaTypeDescriptor attributeJtd; private final AttributeClassification attributeClassification; @@ -44,14 +44,14 @@ public abstract class AbstractAttribute implements PersistentAttribute declaringType, String name, - JavaTypeDescriptor attributeType, + JavaTypeDescriptor attributeJtd, AttributeClassification attributeClassification, SimpleDomainType valueType, Member member, MetadataContext metadataContext) { this.declaringType = declaringType; this.name = name; - this.attributeType = attributeType; + this.attributeJtd = attributeJtd; this.attributeClassification = attributeClassification; this.valueType = valueType; this.member = member; @@ -64,7 +64,7 @@ public abstract class AbstractAttribute implements PersistentAttribute getJavaType() { - return attributeType.getJavaType(); + return attributeJtd.getJavaType(); } public SimpleDomainType getSqmPathType() { @@ -73,7 +73,7 @@ public abstract class AbstractAttribute implements PersistentAttribute getAttributeJavaTypeDescriptor() { - return attributeType; + return attributeJtd; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index b4e9d02824..d3f36603e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -6413,9 +6413,10 @@ public abstract class AbstractEntityPersister EntityMappingType treatTargetType) { visitStateArrayContributors( mapping -> { - if ( mapping.isDeclaredOnTypeOrSuperType( treatTargetType ) ) { +// treat limits are already handled in `#visitAttributeMappings` (called from `#visitStateArrayContributors`) +// if ( mapping.isDeclaredOnTypeOrSuperType( treatTargetType ) ) { fetchableConsumer.accept( mapping ); - } +// } }, treatTargetType ); @@ -6443,7 +6444,7 @@ public abstract class AbstractEntityPersister declaredAttributeMappings.values().forEach( action ); - if ( targetType == null ) { + if ( targetType == null || targetType.isTypeOrSuperType( this ) ) { visitSubTypeAttributeMappings( action ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java index 51f374ac25..1fa0311247 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java @@ -21,6 +21,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.type.BasicType; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; /** diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/AbstractFetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/AbstractFetchParent.java index 168afe9b44..d049482c46 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/AbstractFetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/AbstractFetchParent.java @@ -9,6 +9,7 @@ package org.hibernate.sql.results.internal.domain; import java.util.Collections; import java.util.List; +import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.spi.DomainResultCreationState; import org.hibernate.sql.results.spi.Fetch; @@ -33,6 +34,11 @@ public abstract class AbstractFetchParent implements FetchParent { this.fetches = creationState.visitFetches( this ); } + @Override + public ManagedMappingType getReferencedMappingType() { + return (ManagedMappingType) fetchContainer; + } + @Override public NavigablePath getNavigablePath() { return navigablePath; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/BiDirectionalFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/BiDirectionalFetchImpl.java new file mode 100644 index 0000000000..2054fe5466 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/BiDirectionalFetchImpl.java @@ -0,0 +1,110 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain; + +import java.util.function.Consumer; + +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.BiDirectionalFetch; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Fetchable; +import org.hibernate.sql.results.spi.Initializer; +import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingOptions; +import org.hibernate.sql.results.spi.RowProcessingState; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * Implementation of BiDirectionalFetch for the `oa` case - the bi-dir fetch + * refers to another fetch (`a`) + * + * @author Steve Ebersole + */ +public class BiDirectionalFetchImpl implements BiDirectionalFetch { + private final NavigablePath navigablePath; + private final FetchParent fetchParent; + private final Fetch referencedFetch; + + /** + * Create the bi-dir fetch + * + * @param navigablePath `Person(p).address.owner.address` + * @param fetchParent The parent for the `oa` fetch is `o` + * @param referencedFetch `RootBiDirectionalFetchImpl(a)` (because `a` is itself also a bi-dir fetch referring to the `p root) + */ + public BiDirectionalFetchImpl( + NavigablePath navigablePath, + FetchParent fetchParent, + Fetch referencedFetch) { + this.navigablePath = navigablePath; + this.fetchParent = fetchParent; + this.referencedFetch = referencedFetch; + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public NavigablePath getReferencedPath() { + return referencedFetch.getNavigablePath(); + } + + @Override + public FetchParent getFetchParent() { + return fetchParent; + } + + @Override + public Fetchable getFetchedMapping() { + return referencedFetch.getFetchedMapping(); + } + + @Override + public boolean isNullable() { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public DomainResultAssembler createAssembler( + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + return new CircularFetchAssembler( + getReferencedPath(), + referencedFetch.getFetchedMapping().getJavaTypeDescriptor() + ); + } + + private static class CircularFetchAssembler implements DomainResultAssembler { + private final NavigablePath circularPath; + private final JavaTypeDescriptor javaTypeDescriptor; + + public CircularFetchAssembler( + NavigablePath circularPath, + JavaTypeDescriptor javaTypeDescriptor) { + this.circularPath = circularPath; + this.javaTypeDescriptor = javaTypeDescriptor; + } + + @Override + public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + return rowProcessingState.resolveInitializer( circularPath ).getInitializedInstance(); + } + + @Override + public JavaTypeDescriptor getAssembledJavaTypeDescriptor() { + return javaTypeDescriptor; + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/RootBiDirectionalFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/RootBiDirectionalFetchImpl.java new file mode 100644 index 0000000000..3cdc2c30ca --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/RootBiDirectionalFetchImpl.java @@ -0,0 +1,138 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain; + +import java.util.function.Consumer; + +import org.hibernate.LockMode; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.BiDirectionalFetch; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.EntityResult; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Fetchable; +import org.hibernate.sql.results.spi.Initializer; +import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingOptions; +import org.hibernate.sql.results.spi.RowProcessingState; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Andrea Boriero + */ +public class RootBiDirectionalFetchImpl implements BiDirectionalFetch, Fetchable { + private final NavigablePath navigablePath; + private final FetchParent fetchParent; + private final EntityResult referencedRoot; + + public RootBiDirectionalFetchImpl( + NavigablePath navigablePath, + FetchParent fetchParent, + EntityResult referencedRoot) { + this.fetchParent = fetchParent; + this.referencedRoot = referencedRoot; + this.navigablePath = navigablePath; + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public NavigablePath getReferencedPath() { + return referencedRoot.getNavigablePath(); + } + + @Override + public FetchParent getFetchParent() { + return fetchParent; + } + + @Override + public Fetchable getFetchedMapping() { + return null; + } + + @Override + public boolean isNullable() { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public DomainResultAssembler createAssembler( + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + return new CircularFetchAssembler( + getReferencedPath(), + referencedRoot.getResultJavaTypeDescriptor() + ); + } + + @Override + public String getFetchableName() { + // An entity itself is not fetchable + return null; + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return referencedRoot.getResultJavaTypeDescriptor(); + } + + @Override + public FetchStrategy getMappedFetchStrategy() { + throw new UnsupportedOperationException(); + } + + @Override + public Fetch generateFetch( + FetchParent fetchParent, + FetchTiming fetchTiming, + boolean selected, + LockMode lockMode, + String resultVariable, + DomainResultCreationState creationState) { + throw new UnsupportedOperationException(); + } + + private static class CircularFetchAssembler implements DomainResultAssembler { + private final NavigablePath circularPath; + private final JavaTypeDescriptor javaTypeDescriptor; + + public CircularFetchAssembler( + NavigablePath circularPath, + JavaTypeDescriptor javaTypeDescriptor) { + this.circularPath = circularPath; + this.javaTypeDescriptor = javaTypeDescriptor; + } + + @Override + public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + Initializer initializer = rowProcessingState.resolveInitializer( circularPath ); + if ( initializer.getInitializedInstance() == null ) { + initializer.resolveKey( rowProcessingState ); + initializer.resolveInstance( rowProcessingState ); + initializer.initializeInstance( rowProcessingState ); + } + return initializer.getInitializedInstance(); + } + + @Override + public JavaTypeDescriptor getAssembledJavaTypeDescriptor() { + return javaTypeDescriptor; + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicFetch.java new file mode 100644 index 0000000000..29558ec00f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicFetch.java @@ -0,0 +1,89 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.basic; + +import java.util.function.Consumer; + +import org.hibernate.engine.FetchTiming; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Fetchable; +import org.hibernate.sql.results.spi.Initializer; + +/** + * @author Steve Ebersole + */ +public class BasicFetch implements Fetch { + private final NavigablePath navigablePath; + private final FetchParent fetchParent; + private final BasicValuedModelPart valuedMapping; + private final boolean nullable; + + private final BasicResultAssembler assembler; + + private final FetchTiming fetchTiming; + + public BasicFetch( + int valuesArrayPosition, + FetchParent fetchParent, + BasicValuedModelPart valuedMapping, + boolean nullable, + BasicValueConverter valueConverter, + FetchTiming fetchTiming, + DomainResultCreationState creationState) { + this.nullable = nullable; + this.navigablePath = fetchParent.getNavigablePath().append( valuedMapping.getFetchableName() ); + + this.fetchParent = fetchParent; + this.valuedMapping = valuedMapping; + this.fetchTiming = fetchTiming; + + //noinspection unchecked + this.assembler = new BasicResultAssembler( + valuesArrayPosition, + valuedMapping.getJavaTypeDescriptor(), + valueConverter + ); + + } + + @Override + public FetchParent getFetchParent() { + return fetchParent; + } + + @Override + public Fetchable getFetchedMapping() { + return valuedMapping; + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public boolean isNullable() { + return nullable; + } + + @Override + public DomainResultAssembler createAssembler( + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + return assembler; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/BasicResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java similarity index 97% rename from hibernate-core/src/main/java/org/hibernate/sql/results/internal/BasicResultAssembler.java rename to hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java index d4f10bd672..d6a6b60d93 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/BasicResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html */ -package org.hibernate.sql.results.internal; +package org.hibernate.sql.results.internal.domain.basic; import org.hibernate.Internal; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ScalarDomainResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultImpl.java similarity index 91% rename from hibernate-core/src/main/java/org/hibernate/sql/results/internal/ScalarDomainResultImpl.java rename to hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultImpl.java index 8da1a106aa..6356c48ac8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ScalarDomainResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultImpl.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html */ -package org.hibernate.sql.results.internal; +package org.hibernate.sql.results.internal.domain.basic; import java.util.function.Consumer; @@ -20,7 +20,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * @author Steve Ebersole */ -public class ScalarDomainResultImpl implements ScalarDomainResult { +public class BasicResultImpl implements ScalarDomainResult { private final String resultVariable; private final JavaTypeDescriptor javaTypeDescriptor; @@ -28,14 +28,14 @@ public class ScalarDomainResultImpl implements ScalarDomainResult { private final DomainResultAssembler assembler; - public ScalarDomainResultImpl( + public BasicResultImpl( int jdbcValuesArrayPosition, String resultVariable, JavaTypeDescriptor javaTypeDescriptor) { this( jdbcValuesArrayPosition, resultVariable, javaTypeDescriptor, (NavigablePath) null ); } - public ScalarDomainResultImpl( + public BasicResultImpl( int jdbcValuesArrayPosition, String resultVariable, JavaTypeDescriptor javaTypeDescriptor, @@ -48,7 +48,7 @@ public class ScalarDomainResultImpl implements ScalarDomainResult { this.assembler = new BasicResultAssembler<>( jdbcValuesArrayPosition, javaTypeDescriptor ); } - public ScalarDomainResultImpl( + public BasicResultImpl( int valuesArrayPosition, String resultVariable, JavaTypeDescriptor javaTypeDescriptor, @@ -56,7 +56,7 @@ public class ScalarDomainResultImpl implements ScalarDomainResult { this( valuesArrayPosition, resultVariable, javaTypeDescriptor, valueConverter, null ); } - public ScalarDomainResultImpl( + public BasicResultImpl( int valuesArrayPosition, String resultVariable, JavaTypeDescriptor javaTypeDescriptor, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityMappingNode.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityMappingNode.java index 726a33d81a..610be94ae8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityMappingNode.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityMappingNode.java @@ -92,16 +92,16 @@ public abstract class AbstractEntityMappingNode extends AbstractFetchParent impl ); } - entityDescriptor.visitAttributeMappings( - mapping -> attributeDomainResults.add( - mapping.createDomainResult( - navigablePath.append( mapping.getAttributeName() ), - entityTableGroup, - null, - creationState - ) - ) - ); +// entityDescriptor.visitAttributeMappings( +// mapping -> attributeDomainResults.add( +// mapping.createDomainResult( +// navigablePath.append( mapping.getAttributeName() ), +// entityTableGroup, +// null, +// creationState +// ) +// ) +// ); // todo (6.0) : handle other special navigables such as discriminator, row-id, tenant-id, etc } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/BiDirectionalFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/BiDirectionalFetch.java new file mode 100644 index 0000000000..67a23a084a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/BiDirectionalFetch.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.spi; + +import org.hibernate.query.NavigablePath; + +/** + * @asciidoc + * + * Marker interface for Fetches that are actually references to + * another fetch based on "normalized navigable path" + * + * The following query is used throughout the javadocs for these impls + * to help describe what it going on and why certain methods do certain things. + * + * ```` + * @Entity + * class Person { + * ... + * @ManyToOne(mappedBy="owner") + * Address getAddress() {...} + * } + * + * @Entity + * class Address { + * ... + * @ManyToOne + * Person getOwner() {...} + * } + * + * from Person p + * join fetch p.address a + * join fetch a.owner o + * join fetch o.address oa + * ```` + * + * Here we have one root result and 3 fetches. 2 of the fetches are bi-directional: + * + * `o`:: The paths `p` and `p.address.owner` (aliased as `o`) are the same table reference in SQL terms + * `oa`:: The paths `p.address` and `p.address.owner.address` (aliased as `oa`) are again the same table reference + * + * @author Steve Ebersole + */ +public interface BiDirectionalFetch extends Fetch { + /** + * The NavigablePath for the DomainResult or Fetch that this Fetch refers to. + * + * For `o`, the referenced path is `p`. For `oa`, it's `p.address` + * + * Different from {@link #getNavigablePath()} which returns this fetch's path, i.e. + * `p.address.owner` and `p.address.owner.address` respectively + */ + NavigablePath getReferencedPath(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/CircularFetchDetector.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/CircularFetchDetector.java index bcc395acad..7d72330968 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/CircularFetchDetector.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/CircularFetchDetector.java @@ -7,8 +7,9 @@ package org.hibernate.sql.results.spi; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.internal.domain.BiDirectionalFetchImpl; +import org.hibernate.sql.results.internal.domain.RootBiDirectionalFetchImpl; /** * Maintains state while processing a Fetch graph to be able to detect @@ -19,57 +20,36 @@ import org.hibernate.query.NavigablePath; public class CircularFetchDetector { public Fetch findBiDirectionalFetch(FetchParent fetchParent, Fetchable fetchable) { - // bi-directional references are a special case that need special treatment. - // - // `p.address.resident.homeAddress - // - // what we mean is a fetch path like `a.parent.child.parent`. here the terminal - // `parent` name is the same reference as its parent's (`a.parent.child`) - // parent's (a.parent`) path. - // - // In such a case we want to (mostly) reuse the "parent parent" path fetch - // - // see if we have such a case... - - final NavigablePath parentParentPath = fetchParent.getNavigablePath().getParent(); - if ( parentParentPath != null ) { - if ( fetchable.isCircular( fetchParent ) ) { - if ( fetchParent instanceof Fetch ) { - final FetchParent parentFetchParent = ( (Fetch) fetchParent ).getFetchParent(); - - // we do... - // - // in other words, the `Fetchable`'s `NavigablePath`, relative to its FetchParent here would - // be: - // a.parent.child.parent - // - // it's parentPath is `a.parent.child` so its parentParentPath is `a.parent`. so this Fetchable's - // path is really the same reference as its parentParentPath. This is a special case, handled here... - - // first, this *should* mean we have already "seen" the Fetch generated parentParentPath. So - // look up in the `navigablePathFetchMap` to get that Fetch - - // and use it to create and register the "bi directional" form - - final NavigablePath fetchableNavigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() ); - -// return new BiDirectionalFetchImpl( -// parentFetchParent, -// fetchableNavigablePath -// ); - } - else { -// return new RootBiDirectionalFetchImpl( -// new NavigablePath( fetchable.getJavaTypeDescriptor().getJavaType().getName() ), -// fetchable.getJavaTypeDescriptor(), -// new NavigablePath( fetchable.getNavigableName() ) -// ); - } - } + if ( ! fetchable.isCircular( fetchParent ) ) { + return null; } -// return null; + assert fetchParent instanceof Fetch; + final Fetch fetchParentAsFetch = (Fetch) fetchParent; - throw new NotYetImplementedFor6Exception( getClass() ); + final NavigablePath parentParentPath = fetchParent.getNavigablePath().getParent(); + assert fetchParent.getNavigablePath().getParent() != null; + + assert fetchParentAsFetch.getFetchParent().getNavigablePath().equals( parentParentPath ); + + if ( fetchParentAsFetch.getFetchParent() instanceof Fetch ) { + return new BiDirectionalFetchImpl( + fetchParent.getNavigablePath().append( fetchable.getFetchableName() ), + fetchParent, + fetchParentAsFetch + ); + } + else { + assert fetchParentAsFetch instanceof EntityResult; + + // note : the "`fetchParentAsFetch` is `RootBiDirectionalFetchImpl`" case would + // be handled in the `Fetch` block since `RootBiDirectionalFetchImpl` is a Fetch + + return new RootBiDirectionalFetchImpl( + fetchParent.getNavigablePath().append( fetchable.getFetchableName() ), + fetchParent, + (EntityResult) fetchParentAsFetch + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java index b6f360b366..9aa9326f88 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java @@ -8,6 +8,7 @@ package org.hibernate.sql.results.spi; import java.util.List; +import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.query.NavigablePath; /** @@ -18,6 +19,11 @@ import org.hibernate.query.NavigablePath; public interface FetchParent { FetchableContainer getReferencedMappingContainer(); + /** + * This parent's type + */ + ManagedMappingType getReferencedMappingType(); + /** * Get the property path to this parent */ diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicType.java b/hibernate-core/src/main/java/org/hibernate/type/BasicType.java index a88f4b7784..5ed8e93a6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicType.java @@ -37,6 +37,11 @@ public interface BasicType extends Type, BasicDomainType, MappingType, Bas return this; } + @Override + default JavaTypeDescriptor getJavaTypeDescriptor() { + return getMappedJavaTypeDescriptor(); + } + @Override default JdbcMapping getJdbcMapping() { return this; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index 1c172172e5..eee291455e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -12,7 +12,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; -import org.hibernate.metamodel.model.convert.spi.EnumValueConverter; import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender; import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity; import org.hibernate.query.NavigablePath; @@ -31,15 +30,12 @@ import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcSelect; -import org.hibernate.sql.results.internal.BasicResultAssembler; -import org.hibernate.sql.results.internal.ScalarDomainResultImpl; +import org.hibernate.sql.results.internal.domain.basic.BasicResultAssembler; +import org.hibernate.sql.results.internal.domain.basic.BasicResultImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultAssembler; -import org.hibernate.type.CustomType; -import org.hibernate.type.EnumType; import org.hibernate.type.internal.StandardBasicTypeImpl; -import org.hibernate.usertype.UserType; import org.hibernate.testing.hamcrest.AssignableMatcher; import org.hibernate.testing.orm.junit.DomainModel; @@ -196,8 +192,8 @@ public class SmokeTests { assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) ); final DomainResult domainResult = sqlAst.getDomainResultDescriptors().get( 0 ); - assertThat( domainResult, instanceOf( ScalarDomainResultImpl.class ) ); - final ScalarDomainResultImpl scalarDomainResult = (ScalarDomainResultImpl) domainResult; + assertThat( domainResult, instanceOf( BasicResultImpl.class ) ); + final BasicResultImpl scalarDomainResult = (BasicResultImpl) domainResult; assertThat( scalarDomainResult.getAssembler(), instanceOf( BasicResultAssembler.class ) ); final BasicResultAssembler assembler = (BasicResultAssembler) scalarDomainResult.getAssembler(); assertThat( assembler.getValueConverter(), notNullValue() ); @@ -207,7 +203,7 @@ public class SmokeTests { "e" ).append( "gender" ); assertThat( domainResult.getNavigablePath(), equalTo( expectedSelectedPath ) ); - assertThat( domainResult, instanceOf( ScalarDomainResultImpl.class ) ); + assertThat( domainResult, instanceOf( BasicResultImpl.class ) ); // ScalarDomainResultImpl creates and caches the assembler at its creation. // this just gets access to that cached one