diff --git a/hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java b/hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java index cf21a01398..b43795c8d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java @@ -34,6 +34,7 @@ import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping import org.hibernate.query.EntityIdentifierNavigablePath; import org.hibernate.query.NavigablePath; import org.hibernate.query.internal.FetchMementoBasicStandard; +import org.hibernate.query.internal.ImplicitModelPartResultMemento; import org.hibernate.query.internal.ModelPartResultMementoBasicImpl; import org.hibernate.query.internal.NamedResultSetMappingMementoImpl; import org.hibernate.query.internal.ResultMementoBasicStandard; @@ -46,6 +47,7 @@ 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.ResultsHelper; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** @@ -271,9 +273,6 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr final RuntimeMetamodels runtimeMetamodels = resolutionContext.getSessionFactory().getRuntimeMetamodels(); final EntityMappingType entityDescriptor = runtimeMetamodels.getEntityMappingType( entityName ); final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - final String identifierAttributeName = identifierMapping instanceof SingleAttributeIdentifierMapping - ? ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName() - : EntityIdentifierMapping.ROLE_LOCAL_NAME; final MutableObject identifierMementoReference = new MutableObject<>(); @@ -284,30 +283,65 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr ); final Map fetchMementos = new HashMap<>(); - fetchMappings.forEach( - (attrName, attrMapping) -> { - if ( EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( attrName ) - || identifierAttributeName.equals( attrName ) ) { + + + entityDescriptor.visitAttributeMappings( + attributeMapping -> { + final String fetchableName = attributeMapping.getFetchableName(); + final String attrNameName = attributeMapping.getAttributeName(); + + final boolean isIdentifier = ResultsHelper.isIdentifier( + identifierMapping, + fetchableName, + attrNameName + ); + + final AttributeFetchDescriptor fetchDescriptor = fetchMappings.get( fetchableName ); + + if ( isIdentifier ) { + final ResultMemento idResultMemento; + final EntityIdentifierNavigablePath idPath = new EntityIdentifierNavigablePath( navigablePath ); - identifierMementoReference.set( attrMapping.asResultMemento( idPath, resolutionContext ) ); + if ( fetchDescriptor == null ) { + final AttributeFetchDescriptor idRoleFetchDescriptor = fetchMappings.get( EntityIdentifierMapping.ROLE_LOCAL_NAME ); + if ( idRoleFetchDescriptor == null ) { + idResultMemento = ResultsHelper.implicitIdentifierResult( identifierMapping, idPath, resolutionContext ); + } + else { + idResultMemento = idRoleFetchDescriptor.asResultMemento( idPath, resolutionContext ); + } + } + else { + idResultMemento = fetchDescriptor.asResultMemento( idPath, resolutionContext ); + } + + identifierMementoReference.set( idResultMemento ); } else { - fetchMementos.put( attrName, attrMapping.resolve( resolutionContext ) ); + final NavigablePath fetchPath = navigablePath.append( fetchableName ); + + final FetchMemento fetchMemento; + if ( fetchDescriptor == null ) { + fetchMemento = ResultsHelper.implicitFetch( attributeMapping, fetchPath, resolutionContext ); + } + else { + fetchMemento = fetchDescriptor.resolve( resolutionContext ); + } + + fetchMementos.put( fetchableName, fetchMemento ); } - } + }, + null ); if ( identifierMementoReference.isNotSet() ) { - throw new IllegalStateException( - String.format( - Locale.ROOT, - "Entity identifier mapping not specified for @EntityResult(%s) for ResultSet mapping `%s`", - entityDescriptor.getEntityName(), - resultSetMappingName - ) - ); + final EntityIdentifierNavigablePath idPath = new EntityIdentifierNavigablePath( navigablePath ); + identifierMementoReference.set( new ImplicitModelPartResultMemento( idPath, identifierMapping ) ); } + assert entityDescriptor.getNumberOfAttributeMappings() == fetchMementos.size(); + assert identifierMementoReference.isSet(); + return new ResultMementoEntityStandard( entityDescriptor, LockMode.READ, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java index bdb41e05b5..f738f10df3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java @@ -7,13 +7,14 @@ package org.hibernate.metamodel.mapping; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.sql.results.graph.Fetchable; /** * Describes an attribute at the mapping model level. * * @author Steve Ebersole */ -public interface AttributeMapping extends ModelPart, ValueMapping { +public interface AttributeMapping extends ModelPart, ValueMapping, Fetchable { String getAttributeName(); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitAttributeFetchMemento.java b/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitAttributeFetchMemento.java new file mode 100644 index 0000000000..cb08c27cec --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitAttributeFetchMemento.java @@ -0,0 +1,52 @@ +/* + * 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.internal; + +import java.util.function.Consumer; + +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.MappingType; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.named.FetchMemento; +import org.hibernate.query.results.FetchBuilder; +import org.hibernate.query.results.ImplicitAttributeFetchBuilder; + +/** + * @author Steve Ebersole + */ +public class ImplicitAttributeFetchMemento implements FetchMemento { + private final NavigablePath navigablePath; + private final AttributeMapping attributeMapping; + + public ImplicitAttributeFetchMemento(NavigablePath navigablePath, AttributeMapping attributeMapping) { + this.navigablePath = navigablePath; + this.attributeMapping = attributeMapping; + } + + @Override + public MappingType getMappingType() { + return attributeMapping.getPartMappingType(); + } + + @Override + public FetchBuilder resolve( + Parent parent, + Consumer querySpaceConsumer, + ResultSetMappingResolutionContext context) { + return new ImplicitAttributeFetchBuilder( navigablePath, attributeMapping ); + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public AttributeMapping getReferencedModelPart() { + return attributeMapping; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitModelPartResultMemento.java b/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitModelPartResultMemento.java new file mode 100644 index 0000000000..b0b6e7268e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/ImplicitModelPartResultMemento.java @@ -0,0 +1,51 @@ +/* + * 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.internal; + +import java.util.function.Consumer; + +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.named.ModelPartResultMemento; +import org.hibernate.query.named.ModelPartResultMementoBasic; +import org.hibernate.query.results.ImplicitModelPartResultBuilder; +import org.hibernate.query.results.ResultBuilder; + +/** + * @author Steve Ebersole + */ +public class ImplicitModelPartResultMemento implements ModelPartResultMemento { + private final NavigablePath navigablePath; + private final ModelPart referencedModelPart; + + public ImplicitModelPartResultMemento(NavigablePath navigablePath, ModelPart referencedModelPart) { + this.navigablePath = navigablePath; + this.referencedModelPart = referencedModelPart; + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public ModelPart getReferencedModelPart() { + return referencedModelPart; + } + + @Override + public ResultBuilder resolve( + Consumer querySpaceConsumer, + ResultSetMappingResolutionContext context) { + return new ImplicitModelPartResultBuilder( + navigablePath, + null, + referencedModelPart + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java index f56156e3be..7738412ca6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java @@ -34,6 +34,7 @@ import org.hibernate.sql.results.ResultsLogger; 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 org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; @@ -47,8 +48,9 @@ public class DomainResultCreationStateImpl private final String stateIdentifier; private final FromClauseAccessImpl fromClauseAccess; + private final JdbcValuesMetadata jdbcResultsMetadata; private final Consumer sqlSelectionConsumer; - private final Map sqlSelectionMap = new HashMap<>(); + private final Map sqlSelectionMap = new HashMap<>(); private boolean allowPositionalSelections = true; private final SqlAliasBaseManager sqlAliasBaseManager; @@ -59,10 +61,12 @@ public class DomainResultCreationStateImpl public DomainResultCreationStateImpl( String stateIdentifier, + JdbcValuesMetadata jdbcResultsMetadata, Map> legacyFetchBuilders, Consumer sqlSelectionConsumer, SessionFactoryImplementor sessionFactory) { this.stateIdentifier = stateIdentifier; + this.jdbcResultsMetadata = jdbcResultsMetadata; this.sqlSelectionConsumer = sqlSelectionConsumer; this.fromClauseAccess = new FromClauseAccessImpl(); this.sqlAliasBaseManager = new SqlAliasBaseManager(); @@ -93,6 +97,11 @@ public class DomainResultCreationStateImpl this.allowPositionalSelections = false; } + public JdbcValuesMetadata getJdbcResultsMetadata() { + return jdbcResultsMetadata; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // DomainResultCreationState @@ -200,14 +209,19 @@ public class DomainResultCreationStateImpl } else if ( created instanceof ColumnReference ) { final ColumnReference columnReference = (ColumnReference) created; + final String columnExpression = columnReference.getColumnExpression(); + final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( columnExpression ); + final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition ); final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( - sqlSelectionMap.size() + 1, + valuesArrayPosition, columnReference.getJdbcMapping() ); sqlSelectionMap.put( key, sqlSelection ); sqlSelectionConsumer.accept( sqlSelection ); + + return sqlSelection; } return created; @@ -218,6 +232,9 @@ public class DomainResultCreationStateImpl Expression expression, JavaTypeDescriptor javaTypeDescriptor, TypeConfiguration typeConfiguration) { + if ( expression == null ) { + throw new IllegalArgumentException( "Expression cannot be null" ); + } assert expression instanceof SqlSelectionImpl; return (SqlSelection) expression; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java new file mode 100644 index 0000000000..f6b9705f27 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java @@ -0,0 +1,54 @@ +/* + * 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; + +import java.util.function.BiFunction; + +import org.hibernate.LockMode; +import org.hibernate.engine.FetchTiming; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.query.NavigablePath; +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; + +/** + * FetchBuilder used when an explicit mapping was not given + * + * @author Steve Ebersole + */ +public class ImplicitAttributeFetchBuilder implements FetchBuilder { + private final NavigablePath navigablePath; + private final AttributeMapping attributeMapping; + + public ImplicitAttributeFetchBuilder(NavigablePath navigablePath, AttributeMapping attributeMapping) { + this.navigablePath = navigablePath; + this.attributeMapping = attributeMapping; + } + + @Override + public Fetch buildFetch( + FetchParent parent, + NavigablePath fetchPath, + JdbcValuesMetadata jdbcResultsMetadata, + BiFunction legacyFetchResolver, + DomainResultCreationState domainResultCreationState) { + assert fetchPath.equals( navigablePath ); + + return attributeMapping.generateFetch( + parent, + fetchPath, + FetchTiming.IMMEDIATE, + true, + LockMode.READ, + null, + domainResultCreationState + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitModelPartResultBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitModelPartResultBuilder.java new file mode 100644 index 0000000000..eda00d28ea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitModelPartResultBuilder.java @@ -0,0 +1,55 @@ +/* + * 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; + +import java.util.function.BiFunction; + +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.query.NavigablePath; +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.jdbc.spi.JdbcValuesMetadata; + +/** + * @author Steve Ebersole + */ +public class ImplicitModelPartResultBuilder implements ResultBuilder { + private final NavigablePath navigablePath; + private final String alias; + private final ModelPart referencedModelPart; + + public ImplicitModelPartResultBuilder( + NavigablePath navigablePath, + String alias, + ModelPart referencedModelPart) { + this.navigablePath = navigablePath; + this.alias = alias; + this.referencedModelPart = referencedModelPart; + } + + @Override + public DomainResult buildResult( + JdbcValuesMetadata jdbcResultsMetadata, + int resultPosition, + BiFunction legacyFetchResolver, + DomainResultCreationState domainResultCreationState) { + final DomainResultCreationStateImpl impl = ResultsHelper.impl( domainResultCreationState ); + + ResultsHelper.impl( domainResultCreationState ).disallowPositionalSelections(); + ; + return referencedModelPart.createDomainResult( + navigablePath, + impl.getFromClauseAccess().resolveTableGroup( + navigablePath, + np -> impl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ) + ), + alias, + domainResultCreationState + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java index 346f706db8..b99528aea7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java @@ -115,6 +115,7 @@ public class ResultSetMappingImpl implements ResultSetMapping { final DomainResultCreationStateImpl creationState = new DomainResultCreationStateImpl( mappingIdentifier, + jdbcResultsMetadata, legacyFetchBuilders, sqlSelections::add, sessionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ResultsHelper.java b/hibernate-core/src/main/java/org/hibernate/query/results/ResultsHelper.java index 738345960f..24bf6d297b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ResultsHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ResultsHelper.java @@ -6,7 +6,21 @@ */ package org.hibernate.query.results; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; +import org.hibernate.query.EntityIdentifierNavigablePath; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.internal.ImplicitAttributeFetchMemento; +import org.hibernate.query.internal.ImplicitModelPartResultMemento; +import org.hibernate.query.internal.ModelPartResultMementoBasicImpl; +import org.hibernate.query.internal.ResultSetMappingResolutionContext; +import org.hibernate.query.named.FetchMemento; +import org.hibernate.query.named.ResultMemento; +import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.Fetch; /** * @author Steve Ebersole @@ -36,4 +50,37 @@ public class ResultsHelper { private ResultsHelper() { } + + public static boolean isIdentifier(EntityIdentifierMapping identifierDescriptor, String... names) { + final String identifierAttributeName = identifierDescriptor instanceof SingleAttributeIdentifierMapping + ? ( (SingleAttributeIdentifierMapping) identifierDescriptor ).getAttributeName() + : EntityIdentifierMapping.ROLE_LOCAL_NAME; + + for ( int i = 0; i < names.length; i++ ) { + final String name = names[ i ]; + if ( EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( name ) ) { + return true; + } + + if ( identifierAttributeName.equals( name ) ) { + return true; + } + } + + return false; + } + + public static ResultMemento implicitIdentifierResult( + EntityIdentifierMapping identifierMapping, + EntityIdentifierNavigablePath idPath, + ResultSetMappingResolutionContext resolutionContext) { + return new ImplicitModelPartResultMemento( idPath, identifierMapping ); + } + + public static FetchMemento implicitFetch( + AttributeMapping attributeMapping, + NavigablePath attributePath, + ResultSetMappingResolutionContext resolutionContext) { + return new ImplicitAttributeFetchMemento( attributePath, attributeMapping ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstCreationStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstCreationStateImpl.java index db30892ce1..234f54a605 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstCreationStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstCreationStateImpl.java @@ -37,7 +37,7 @@ public class SqlAstCreationStateImpl implements SqlAstCreationState, SqlAstProce private final SqlAliasBaseManager sqlAliasBaseManager; private final Consumer sqlSelectionConsumer; - private final Map sqlSelectionMap = new HashMap<>(); + private final Map sqlSelectionMap = new HashMap<>(); private final SessionFactoryImplementor sessionFactory; diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java index b97c29f293..9702e5d5ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/SqlAstProcessingStateImpl.java @@ -29,7 +29,7 @@ import org.hibernate.type.spi.TypeConfiguration; public class SqlAstProcessingStateImpl implements SqlAstProcessingState, SqlExpressionResolver { private final FromClauseAccessImpl fromClauseAccess; - private final Map sqlSelectionMap = new HashMap<>(); + private final Map sqlSelectionMap = new HashMap<>(); private final Consumer sqlSelectionConsumer; private final SqlAstCreationState sqlAstCreationState; diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderAttribute.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderAttribute.java index 8dc09c49c2..15035227b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderAttribute.java @@ -11,7 +11,6 @@ import java.util.function.BiFunction; import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping; -import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.SqlSelectionImpl; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.results.graph.DomainResult; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BaseUsageTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BaseUsageTest.java new file mode 100644 index 0000000000..e8c567c973 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BaseUsageTest.java @@ -0,0 +1,38 @@ +/* + * 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.orm.test.query.resultmapping; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +/** + * @author Steve Ebersole + */ +@DomainModel( annotatedClasses = SimpleEntityWithNamedMappings.class ) +@SessionFactory +public class BaseUsageTest { + @BeforeEach + public void prepareData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.save( new SimpleEntityWithNamedMappings( 1, "test" ) ); + } + ); + } + + @AfterEach + public void cleanUpData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete SimpleEntityWithNamedMappings" ).executeUpdate(); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BasicResultTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BasicResultTests.java new file mode 100644 index 0000000000..2165181af6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/BasicResultTests.java @@ -0,0 +1,68 @@ +/* + * 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.orm.test.query.resultmapping; + +import java.util.List; + +import org.hibernate.query.named.NamedResultSetMappingMemento; + +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; + +/** + * @author Steve Ebersole + */ +public class BasicResultTests extends BaseUsageTest { + @Test + public void testSimpleScalarMapping(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + // make sure it is in the repository + final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() + .getQueryEngine() + .getNamedQueryRepository() + .getResultSetMappingMemento( "name" ); + assertThat( mappingMemento, notNullValue() ); + + // apply it to a native-query + final String qryString = "select name from SimpleEntityWithNamedMappings"; + final List names = session.createNativeQuery( qryString, "name" ).list(); + + assertThat( names.size(), is( 1 ) ); + assertThat( names.get( 0 ), is( "test" ) ); + + // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented + session.createStoredProcedureCall( "abc", "name" ); + } + ); + } + + @Test + public void testCompoundScalarMapping(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + // make sure it is in the repository + final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() + .getQueryEngine() + .getNamedQueryRepository() + .getResultSetMappingMemento( "id_name" ); + assertThat( mappingMemento, notNullValue() ); + + // apply it to a native-query + final String qryString = "select id, name from SimpleEntityWithNamedMappings"; + session.createNativeQuery( qryString, "id_name" ).list(); + + // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented + session.createStoredProcedureCall( "abc", "id_name" ); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/UsageTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/EntityResultTests.java similarity index 56% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/UsageTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/EntityResultTests.java index 91632bc998..4a21c82a99 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/UsageTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/EntityResultTests.java @@ -4,18 +4,13 @@ * 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.orm.test.query.named.resultmapping; +package org.hibernate.orm.test.query.resultmapping; import java.util.List; import org.hibernate.query.named.NamedResultSetMappingMemento; -import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.FailureExpected; -import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -25,57 +20,7 @@ import static org.hamcrest.Matchers.notNullValue; /** * @author Steve Ebersole */ -@DomainModel( annotatedClasses = SimpleEntityWithNamedMappings.class ) -@SessionFactory -public class UsageTests { - // select a.{*} - @Test - public void testSimpleScalarMapping(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - // make sure it is in the repository - final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() - .getQueryEngine() - .getNamedQueryRepository() - .getResultSetMappingMemento( "id_name" ); - assertThat( mappingMemento, notNullValue() ); - - // apply it to a native-query - final String qryString = "select id, name from SimpleEntityWithNamedMappings"; - session.createNativeQuery( qryString, "id_name" ).list(); - - // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented - session.createStoredProcedureCall( "abc", "id_name" ); - } - ); - } - - @Test - public void testSimpleInstantiationOfScalars(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - // make sure it is in the repository - final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() - .getQueryEngine() - .getNamedQueryRepository() - .getResultSetMappingMemento( "id_name_dto" ); - assertThat( mappingMemento, notNullValue() ); - - // apply it to a native-query - final String qryString = "select id, name from SimpleEntityWithNamedMappings"; - final List results - = session.createNativeQuery( qryString, "id_name_dto" ).list(); - assertThat( results.size(), is( 1 ) ); - - final SimpleEntityWithNamedMappings.DropDownDto dto = results.get( 0 ); - assertThat( dto.getId(), is( 1 ) ); - assertThat( dto.getText(), is( "test" ) ); - - // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented - session.createStoredProcedureCall( "abc", "id_name_dto" ); - } - ); - } +public class EntityResultTests extends BaseUsageTest { @Test public void testSimpleEntityResultMapping(SessionFactoryScope scope) { @@ -90,8 +35,9 @@ public class UsageTests { // apply it to a native-query final String qryString = "select id, name, notes from SimpleEntityWithNamedMappings"; - final List results - = session.createNativeQuery( qryString, "entity" ).list(); + final List results = session + .createNativeQuery( qryString, "entity" ) + .list(); assertThat( results.size(), is( 1 ) ); final SimpleEntityWithNamedMappings entity = results.get( 0 ); @@ -104,20 +50,60 @@ public class UsageTests { ); } - @BeforeEach - public void prepareData(SessionFactoryScope scope) { + @Test + public void testImplicitAttributeMapping(SessionFactoryScope scope) { scope.inTransaction( session -> { - session.save( new SimpleEntityWithNamedMappings( 1, "test" ) ); + // make sure it is in the repository + final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() + .getQueryEngine() + .getNamedQueryRepository() + .getResultSetMappingMemento( + "entity-none" ); + assertThat( mappingMemento, notNullValue() ); + + // apply it to a native-query + final String qryString = "select id, name, notes from SimpleEntityWithNamedMappings"; + final List results = session + .createNativeQuery( qryString, "entity-none" ) + .list(); + assertThat( results.size(), is( 1 ) ); + + final SimpleEntityWithNamedMappings entity = results.get( 0 ); + assertThat( entity.getId(), is( 1 ) ); + assertThat( entity.getName(), is( "test" ) ); + + // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented + session.createStoredProcedureCall( "abc", "entity-none" ); } ); } - @AfterEach - public void cleanUpData(SessionFactoryScope scope) { + @Test + public void testMixedAttributeMapping(SessionFactoryScope scope) { scope.inTransaction( session -> { - session.createQuery( "delete SimpleEntityWithNamedMappings" ).executeUpdate(); + // make sure it is in the repository + final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() + .getQueryEngine() + .getNamedQueryRepository() + .getResultSetMappingMemento( + "entity-id-notes" ); + assertThat( mappingMemento, notNullValue() ); + + // apply it to a native-query + final String qryString = "select id, name, notes from SimpleEntityWithNamedMappings"; + final List results = session + .createNativeQuery( qryString, "entity-id-notes" ) + .list(); + assertThat( results.size(), is( 1 ) ); + + final SimpleEntityWithNamedMappings entity = results.get( 0 ); + assertThat( entity.getId(), is( 1 ) ); + assertThat( entity.getName(), is( "test" ) ); + + // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented + session.createStoredProcedureCall( "abc", "entity-id-notes" ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/InstantiationResultTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/InstantiationResultTests.java new file mode 100644 index 0000000000..b589b81549 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/InstantiationResultTests.java @@ -0,0 +1,53 @@ +/* + * 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.orm.test.query.resultmapping; + +import java.util.List; + +import org.hibernate.query.named.NamedResultSetMappingMemento; + +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; + +/** + * @author Steve Ebersole + */ +public class InstantiationResultTests extends BaseUsageTest { + + @Test + public void testSimpleInstantiationOfScalars(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + // make sure it is in the repository + final NamedResultSetMappingMemento mappingMemento = session.getSessionFactory() + .getQueryEngine() + .getNamedQueryRepository() + .getResultSetMappingMemento( + "id_name_dto" ); + assertThat( mappingMemento, notNullValue() ); + + // apply it to a native-query + final String qryString = "select id, name from SimpleEntityWithNamedMappings"; + final List results = session.createNativeQuery( qryString, "id_name_dto" ).list(); + assertThat( results.size(), is( 1 ) ); + + final SimpleEntityWithNamedMappings.DropDownDto dto = + (SimpleEntityWithNamedMappings.DropDownDto) results.get( 0 ); + + assertThat( dto.getId(), is( 1 ) ); + assertThat( dto.getText(), is( "test" ) ); + + // todo (6.0) : should also try executing the ProcedureCall once that functionality is implemented + session.createStoredProcedureCall( "abc", "id_name_dto" ); + } + ); + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleEntityWithNamedMappings.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleEntityWithNamedMappings.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleEntityWithNamedMappings.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleEntityWithNamedMappings.java index 804d867eee..c8743ea509 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleEntityWithNamedMappings.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleEntityWithNamedMappings.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.orm.test.query.named.resultmapping; +package org.hibernate.orm.test.query.resultmapping; import javax.persistence.Basic; import javax.persistence.ColumnResult; @@ -51,6 +51,12 @@ import javax.persistence.SqlResultSetMapping; } ) ) +@SqlResultSetMapping( + name = "entity-none", + entities = @EntityResult( + entityClass = SimpleEntityWithNamedMappings.class + ) +) @SqlResultSetMapping( name = "entity-id-notes", entities = @EntityResult( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleNamedMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleNamedMappingTests.java similarity index 81% rename from hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleNamedMappingTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleNamedMappingTests.java index d6548315df..6a70a3e247 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/named/resultmapping/SimpleNamedMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/SimpleNamedMappingTests.java @@ -4,12 +4,13 @@ * 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.orm.test.query.named.resultmapping; +package org.hibernate.orm.test.query.resultmapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.internal.ResultSetMappingResolutionContext; import org.hibernate.query.named.NamedQueryRepository; import org.hibernate.query.named.NamedResultSetMappingMemento; +import org.hibernate.query.results.ResultBuilderBasicValued; import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.results.ResultSetMappingImpl; import org.hibernate.query.spi.QueryEngine; @@ -19,6 +20,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -43,8 +45,15 @@ public class SimpleNamedMappingTests { } }; - mappingMemento.resolve( mapping, querySpace -> {}, resolutionContext ); + mappingMemento.resolve( mapping, querySpace -> { + }, resolutionContext ); assertThat( mapping.getNumberOfResultBuilders(), is( 1 ) ); + mapping.visitResultBuilders( + (position, builder) -> { + assertThat( position, is( 0 ) ); + assertThat( builder, instanceOf( ResultBuilderBasicValued.class ) ); + } + ); } }