From 9a0ad0f21de8ed152b1061bfd3145c2a93b03aaa Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 21 Nov 2019 08:38:07 +0000 Subject: [PATCH] work on aggregate composite identifier --- .../MetamodelSelectBuilderProcess.java | 1 + .../mapping/EmbeddableMappingType.java | 11 +- .../mapping/EmbeddedIdentifierMapping.java | 16 + .../EmbeddedIdentifierMappingImpl.java | 284 ++++++++++++++++++ .../internal/MappingModelCreationHelper.java | 113 ++++--- .../entity/AbstractEntityPersister.java | 17 +- .../test/sql/exec/EmbeddedIdEntityTest.java | 123 ++++++++ .../exec/EntityWithNotAggregateIdTest.java | 123 ++++++++ .../gambit/EntityWithNotAggregateId.java | 74 +++++ 9 files changed, 716 insertions(+), 46 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddedIdentifierMapping.java create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EmbeddedIdEntityTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EntityWithNotAggregateIdTest.java create mode 100644 hibernate-testing/src/main/java/org/hibernate/testing/orm/domain/gambit/EntityWithNotAggregateId.java diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java index 8444aab374..17aedd2b41 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java @@ -277,6 +277,7 @@ public class MetamodelSelectBuilderProcess { final ColumnReference columnReference = columnReferences.get( j ); final JdbcParameter jdbcParameter = new JdbcParameterImpl( columnReference.getJdbcMapping() ); jdbcParameterConsumer.accept( jdbcParameter ); + tupleParams.add( jdbcParameter ); } final SqlTuple paramTuple = new SqlTuple( tupleParams, keyPart ); predicate.addExpression( paramTuple ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java index d2e7f0e540..646766cb63 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java @@ -279,7 +279,16 @@ public class EmbeddableMappingType implements ManagedMappingType { JdbcValuesConsumer consumer, SharedSessionContractImplementor session) { attributeMappings.forEach( - (s, attributeMapping) -> attributeMapping.visitJdbcValues( value, clause, consumer, session ) + (s, attributeMapping) -> { + Object o = attributeMapping.getPropertyAccess().getGetter().get( value ); + attributeMapping.visitJdbcValues( o, clause, consumer, session ); + } + ); + } + + public void visitColumns(ColumnConsumer consumer) { + attributeMappings.values().forEach( + attributeMapping -> attributeMapping.visitColumns( consumer ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddedIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddedIdentifierMapping.java new file mode 100644 index 0000000000..64b7addc97 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddedIdentifierMapping.java @@ -0,0 +1,16 @@ +/* + * 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.metamodel.mapping; + +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; + +/** + * @author Andrea Boriero + */ +public interface EmbeddedIdentifierMapping extends EntityIdentifierMapping, EmbeddableValuedModelPart { +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java new file mode 100644 index 0000000000..d0c488b7ad --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java @@ -0,0 +1,284 @@ +/* + * 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.metamodel.mapping.internal; + +import java.util.Arrays; +import java.util.List; +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.internal.util.collections.CollectionHelper; +import org.hibernate.metamodel.mapping.ColumnConsumer; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.EmbeddedIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityMappingType; +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.SingularAttributeMapping; +import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; +import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; +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.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.CompositeTableGroup; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; +import org.hibernate.sql.results.internal.domain.composite.CompositeFetch; +import org.hibernate.sql.results.internal.domain.composite.CompositeResult; +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.spi.TypeConfiguration; + +/** + * @author Andrea Boriero + */ +public class EmbeddedIdentifierMappingImpl extends AbstractStateArrayContributorMapping + implements EmbeddedIdentifierMapping { + private final PropertyAccess propertyAccess; + private final String tableExpression; + private final String[] attrColumnNames; + + public EmbeddedIdentifierMappingImpl( + String name, + MappingType type, + StateArrayContributorMetadataAccess attributeMetadataAccess, + FetchStrategy mappedFetchStrategy, + int stateArrayPosition, + ManagedMappingType declaringType, + PropertyAccess propertyAccess, + String tableExpression, + String[] attrColumnNames) { + super( name, type, attributeMetadataAccess, mappedFetchStrategy, stateArrayPosition, declaringType ); + this.propertyAccess = propertyAccess; + this.tableExpression = tableExpression; + this.attrColumnNames = attrColumnNames; + } + + @Override + public String getPartName() { + return getAttributeName(); + } + + @Override + public PropertyAccess getPropertyAccess() { + return propertyAccess; + } + + @Override + public EmbeddableMappingType getEmbeddableTypeDescriptor() { + return getMappedTypeDescriptor(); + } + + @Override + public EmbeddableMappingType getMappedTypeDescriptor() { + return (EmbeddableMappingType) super.getMappedTypeDescriptor(); + } + + @Override + public String getContainingTableExpression() { + return tableExpression; + } + + @Override + public List getMappedColumnExpressions() { + return Arrays.asList( attrColumnNames ); + } + + @Override + public SingularAttributeMapping getParentInjectionAttributeMapping() { + return null; + } + + @Override + public void visitJdbcTypes( + Consumer action, + Clause clause, + TypeConfiguration typeConfiguration) { + getMappedTypeDescriptor().visitJdbcTypes( action,clause,typeConfiguration ); + } + + @Override + public void visitJdbcValues( + Object value, Clause clause, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) { + getEmbeddableTypeDescriptor().visitJdbcValues( value, clause, valuesConsumer, session ); + } + + + + @Override + public DomainResult createDomainResult( + NavigablePath navigablePath, + TableGroup tableGroup, + String resultVariable, + DomainResultCreationState creationState) { + return new CompositeResult<>( + navigablePath, + this, + resultVariable, + creationState + ); + } + + @Override + public Expression toSqlExpression( + TableGroup tableGroup, + Clause clause, + SqmToSqlAstConverter walker, + SqlAstCreationState sqlAstCreationState) { + final List columnReferences = CollectionHelper.arrayList( attrColumnNames.length ); + final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() ); + getEmbeddableTypeDescriptor().visitJdbcTypes( + new Consumer() { + private int index = 0; + + @Override + public void accept(JdbcMapping jdbcMapping) { + final String attrColumnExpr = attrColumnNames[ index++ ]; + + final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression( + SqlExpressionResolver.createColumnReferenceKey( + tableReference, + attrColumnExpr + ), + sqlAstProcessingState -> new ColumnReference( + tableReference.getIdentificationVariable(), + attrColumnExpr, + jdbcMapping, + sqlAstCreationState.getCreationContext().getSessionFactory() + ) + ); + + columnReferences.add( (ColumnReference) columnReference ); + } + }, + clause, + sqlAstCreationState.getCreationContext().getSessionFactory().getTypeConfiguration() + ); + + return new SqlTuple( columnReferences, this ); + + } + + @Override + public TableGroupJoin createTableGroupJoin( + NavigablePath navigablePath, + TableGroup lhs, + String explicitSourceAlias, + JoinType joinType, + LockMode lockMode, + SqlAliasBaseGenerator aliasBaseGenerator, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + final CompositeTableGroup compositeTableGroup = new CompositeTableGroup( + navigablePath, + this, + lhs + ); + + lhs.addTableGroupJoin( new TableGroupJoin( navigablePath, JoinType.INNER, compositeTableGroup, null ) ); + + return new TableGroupJoin( + navigablePath, + joinType, + compositeTableGroup + ); + } + + @Override + public String getSqlAliasStem() { + return getAttributeName(); + } + + @Override + public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { + return getMappedTypeDescriptor().findSubPart( name, treatTargetType ); + } + + @Override + public void visitSubParts( + Consumer consumer, EntityMappingType treatTargetType) { + getMappedTypeDescriptor().visitSubParts( consumer, treatTargetType ); + } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + getEmbeddableTypeDescriptor().visitAttributeMappings( + attrMapping -> { + if ( attrMapping instanceof TableGroupProducer ) { + ( (TableGroupProducer) attrMapping ).applyTableReferences( + sqlAliasBase, + baseJoinType, + collector, + sqlExpressionResolver, + creationContext + ); + } + else if ( attrMapping.getMappedTypeDescriptor() instanceof TableGroupProducer ) { + ( (TableGroupProducer) attrMapping.getMappedTypeDescriptor() ).applyTableReferences( + sqlAliasBase, + baseJoinType, + collector, + sqlExpressionResolver, + creationContext + ); + } + } + ); + } + + @Override + public Fetch generateFetch( + FetchParent fetchParent, + NavigablePath fetchablePath, + FetchTiming fetchTiming, + boolean selected, + LockMode lockMode, + String resultVariable, + DomainResultCreationState creationState) { + return new CompositeFetch( + fetchablePath, + this, + fetchParent, + fetchTiming, + getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), + creationState + ); + } + + @Override + public int getNumberOfFetchables() { + return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(); + } + + public void visitColumns(ColumnConsumer consumer) { + getEmbeddableTypeDescriptor().visitColumns( consumer ); + } +} 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 7f97cb327d..20ed9afe1a 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 @@ -283,6 +283,8 @@ public class MappingModelCreationHelper { public static EntityIdentifierMapping buildEncapsulatedCompositeIdentifierMapping( EntityPersister entityPersister, + Property bootProperty, + String attributeName, String rootTableName, String[] rootTableKeyColumnNames, CompositeType cidType, @@ -294,48 +296,29 @@ public class MappingModelCreationHelper { final PropertyAccess propertyAccess = entityPersister.getRepresentationStrategy() .resolvePropertyAccess( bootEntityDescriptor.getIdentifierProperty() ); - return new EntityIdentifierMapping() { - @Override - public PropertyAccess getPropertyAccess() { - return propertyAccess; - } + final StateArrayContributorMetadataAccess attributeMetadataAccess = getStateArrayContributorMetadataAccess( + propertyAccess + ); - @Override - public MappingType getMappedTypeDescriptor() { - return ( (BasicValuedModelPart) entityPersister.getIdentifierType() ).getMappedTypeDescriptor(); - } + final EmbeddableMappingType embeddableMappingType = EmbeddableMappingType.from( + (Component) bootProperty.getValue(), + cidType, + attributeMappingType -> new EmbeddedIdentifierMappingImpl( + attributeName, + attributeMappingType, + attributeMetadataAccess, + FetchStrategy.IMMEDIATE_JOIN, + 0, + entityPersister, + propertyAccess, + rootTableName, + rootTableKeyColumnNames + ), + creationProcess + ); - @Override - public JavaTypeDescriptor getJavaTypeDescriptor() { - return getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); - } - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - String resultVariable, - DomainResultCreationState creationState) { - return ( (ModelPart) entityPersister.getIdentifierType() ).createDomainResult( - navigablePath, - tableGroup, - resultVariable, - creationState - ); - } - - @Override - public void applySqlSelections( - NavigablePath navigablePath, - TableGroup tableGroup, - DomainResultCreationState creationState) { - ( (ModelPart) entityPersister.getIdentifierType() ).applySqlSelections( - navigablePath, - tableGroup, - creationState - ); - } - }; + return (EmbeddedIdentifierMappingImpl) embeddableMappingType.getEmbeddedValueMapping(); } public static EntityIdentifierMapping buildNonEncapsulatedCompositeIdentifierMapping( @@ -639,6 +622,58 @@ public class MappingModelCreationHelper { }; } + protected static StateArrayContributorMetadataAccess getStateArrayContributorMetadataAccess( + PropertyAccess propertyAccess) { + return entityMappingType -> new StateArrayContributorMetadata() { + + private final MutabilityPlan mutabilityPlan = ImmutableMutabilityPlan.INSTANCE; + + + @Override + public PropertyAccess getPropertyAccess() { + return propertyAccess; + } + + @Override + public MutabilityPlan getMutabilityPlan() { + return mutabilityPlan; + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public boolean isInsertable() { + return true; + } + + @Override + public boolean isUpdatable() { + return false; + } + + @Override + public boolean isIncludedInDirtyChecking() { + + return false; + } + + @Override + public boolean isIncludedInOptimisticLocking() { + // todo (6.0) : do not sure this is correct + return true; + } + + @Override + public CascadeStyle getCascadeStyle() { + // todo (6.0) : do not sure this is correct + return null; + } + }; + } + public static PluralAttributeMapping buildPluralAttributeMapping( String attrName, int stateArrayPosition, 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 bdc72813c3..b623dfc545 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 @@ -6121,6 +6121,12 @@ public abstract class AbstractEntityPersister return; } + final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext(); + + final PersistentClass bootEntityDescriptor = creationContext + .getBootModel() + .getEntityBinding( getEntityName() ); + if ( superMappingType != null && shouldProcessSuperMapping() ) { ( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess ); @@ -6131,7 +6137,8 @@ public abstract class AbstractEntityPersister else { identifierMapping = creationProcess.processSubPart( EntityIdentifierMapping.ROLE_LOCAL_NAME, - (role, creationProcess1) -> generateIdentifierMapping( creationProcess ) + (role, creationProcess1) -> + generateIdentifierMapping( creationProcess, bootEntityDescriptor.getIdentifierProperty() ) ); if ( getVersionType() == null ) { @@ -6153,11 +6160,7 @@ public abstract class AbstractEntityPersister naturalIdMapping = null; } - final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext(); - final PersistentClass bootEntityDescriptor = creationContext - .getBootModel() - .getEntityBinding( getEntityName() ); final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel(); int stateArrayPosition = getStateArrayInitialPosition( creationProcess ); @@ -6362,7 +6365,7 @@ public abstract class AbstractEntityPersister } - private EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess) { + private EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, Property identifierProperty) { final Type idType = getIdentifierType(); if ( idType instanceof CompositeType ) { @@ -6370,6 +6373,8 @@ public abstract class AbstractEntityPersister if ( ! cidType.isEmbedded() ) { return MappingModelCreationHelper.buildEncapsulatedCompositeIdentifierMapping( this, + identifierProperty, + identifierProperty.getName(), getRootTableName(), rootTableKeyColumnNames, cidType, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EmbeddedIdEntityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EmbeddedIdEntityTest.java new file mode 100644 index 0000000000..5563edb4fb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EmbeddedIdEntityTest.java @@ -0,0 +1,123 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.sql.exec; + +import org.hibernate.stat.spi.StatisticsImplementor; + +import org.hibernate.testing.orm.domain.gambit.EmbeddedIdEntity; +import org.hibernate.testing.orm.domain.gambit.EmbeddedIdEntity.EmbeddedIdEntityId; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +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.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; + +/** + * @author Chris Cranford + */ +@DomainModel( + annotatedClasses = { + EmbeddedIdEntity.class + } +) +@ServiceRegistry +@SessionFactory(generateStatistics = true) +public class EmbeddedIdEntityTest { + + private EmbeddedIdEntityId entityId; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + final EmbeddedIdEntity entity = new EmbeddedIdEntity(); + entityId = new EmbeddedIdEntityId( 25, "Acme" ); + scope.inTransaction( + session -> { + entity.setId( entityId ); + entity.setData( "test" ); + session.save( entity ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + sesison -> + sesison.createQuery( "delete from EmbeddedIdEntity" ).executeUpdate() + ); + } + + @Test + public void testHqlSelectAField(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final String value = session.createQuery( "select e.data FROM EmbeddedIdEntity e", String.class ) + .uniqueResult(); + assertThat( value, is( "test" ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testHqlSelect(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EmbeddedIdEntity loaded = session.createQuery( + "select e FROM EmbeddedIdEntity e", + EmbeddedIdEntity.class + ).uniqueResult(); + assertThat( loaded.getData(), is( "test" ) ); + assertThat( loaded.getId(), equalTo( entityId ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testHqlSelectOnlyTheEmbeddedId(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EmbeddedIdEntityId value = session.createQuery( + "select e.id FROM EmbeddedIdEntity e", + EmbeddedIdEntityId.class + ).uniqueResult(); + assertThat( value, equalTo( entityId ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testGet(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EmbeddedIdEntity loaded = session.get( EmbeddedIdEntity.class, entityId ); + assertThat( loaded, notNullValue() ); + assertThat( loaded.getId(), notNullValue() ); + assertThat( loaded.getId(), equalTo( entityId ) ); + assertThat( loaded.getData(), is( "test" ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EntityWithNotAggregateIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EntityWithNotAggregateIdTest.java new file mode 100644 index 0000000000..00b029a81d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/EntityWithNotAggregateIdTest.java @@ -0,0 +1,123 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.sql.exec; + +import org.hibernate.stat.spi.StatisticsImplementor; + +import org.hibernate.testing.orm.domain.gambit.EntityWithNotAggregateId; +import org.hibernate.testing.orm.domain.gambit.EntityWithNotAggregateId.PK; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.ServiceRegistry; +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.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; + +@DomainModel( + annotatedClasses = { + EntityWithNotAggregateId.class + } +) +@ServiceRegistry +@SessionFactory(generateStatistics = true) +@Disabled(value = "non aggregate composit id has not been yet implemented") +public class EntityWithNotAggregateIdTest { + + private PK entityId; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + final EntityWithNotAggregateId entity = new EntityWithNotAggregateId(); + entityId = new PK( 25, "Acme" ); + scope.inTransaction( + session -> { + entity.setId( entityId ); + entity.setData( "test" ); + session.save( entity ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + sesison -> + sesison.createQuery( "delete from EntityWithIdClass" ).executeUpdate() + ); + } + + @Test + public void testHqlSelectAField(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final String value = session.createQuery( "select e.data FROM EntityWithIdClass e", String.class ) + .uniqueResult(); + assertThat( value, is( "test" ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testHqlSelect(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithNotAggregateId loaded = session.createQuery( + "select e FROM EntityWithIdClass e", + EntityWithNotAggregateId.class + ).uniqueResult(); + assertThat( loaded.getData(), is( "test" ) ); + assertThat( loaded.getId(), equalTo( entityId ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testHqlSelectOnlyTheEmbeddedId(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithNotAggregateId value = session.createQuery( + "select e.id FROM EntityWithIdClass e", + EntityWithNotAggregateId.class + ).uniqueResult(); + assertThat( value, equalTo( entityId ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + + @Test + public void testGet(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithNotAggregateId loaded = session.get( EntityWithNotAggregateId.class, entityId ); + assertThat( loaded, notNullValue() ); + assertThat( loaded.getId(), notNullValue() ); + assertThat( loaded.getId(), equalTo( entityId ) ); + assertThat( loaded.getData(), is( "test" ) ); + } + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/domain/gambit/EntityWithNotAggregateId.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/domain/gambit/EntityWithNotAggregateId.java new file mode 100644 index 0000000000..24679f35c8 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/domain/gambit/EntityWithNotAggregateId.java @@ -0,0 +1,74 @@ +/* + * 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.testing.orm.domain.gambit; + +import java.io.Serializable; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; + +/** + * @author Andrea Boriero + */ +@Entity +@IdClass(EntityWithNotAggregateId.PK.class) +public class EntityWithNotAggregateId { + + @Id + private Integer value1; + + @Id + private String value2; + + private String data; + + public PK getId() { + return new PK( value1, value2 ); + } + + public void setId(PK id) { + this.value1 = id.getValue1(); + this.value2 = id.getValue2(); + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public static class PK implements Serializable { + private Integer value1; + private String value2; + + public PK() { + } + + public PK(Integer value1, String value2) { + this.value1 = value1; + this.value2 = value2; + } + + public Integer getValue1() { + return value1; + } + + public void setValue1(Integer value1) { + this.value1 = value1; + } + + public String getValue2() { + return value2; + } + + public void setValue2(String value2) { + this.value2 = value2; + } + } +}