From a9ee082128edc354032038ab9e9634dab19753a6 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 7 Jan 2020 15:34:29 -0600 Subject: [PATCH] initial work on non-aggregated composite-id support --- .../internal/DefaultLoadEventListener.java | 70 +++--- .../internal/DynamicMapInstantiator.java | 3 +- .../OptimizedPojoInstantiatorImpl.java | 4 +- .../internal/PojoInstantiatorImpl.java | 4 +- .../mapping/BasicEntityIdentifierMapping.java | 4 +- .../mapping/CompositeIdentifierMapping.java | 26 ++ .../mapping/EmbeddedIdentifierMapping.java | 16 -- .../mapping/EntityIdentifierMapping.java | 5 +- .../metamodel/mapping/EntityMappingType.java | 5 + .../EmbeddedIdentifierMappingImpl.java | 52 +++- .../internal/MappingModelCreationHelper.java | 92 +++----- .../NonAggregatedIdentifierMappingImpl.java | 111 +++++++++ .../SingleAttributeIdentifierMapping.java | 20 ++ .../domain/AbstractIdentifiableType.java | 8 +- .../internal/CompositeSqmPathSource.java | 15 ++ .../internal/EmbeddedSqmPathSource.java | 5 +- .../domain/internal/MappingMetamodelImpl.java | 2 +- .../NonAggregatedCompositeSqmPathSource.java | 44 ++++ .../hibernate/metamodel/spi/Instantiator.java | 3 +- .../entity/AbstractEntityPersister.java | 120 ++++++++-- .../sqm/sql/BaseSqmToSqlAstConverter.java | 3 +- .../AbstractEmbeddableInitializer.java | 4 +- .../mapping/cid/aggregated/SmokeTests.java | 187 +++++++++++++++ .../mapping/cid/nonaggregated/SmokeTests.java | 222 ++++++++++++++++++ .../metamodel/mapping/cid/package-info.java | 11 + .../annotations/access/xml/XmlAccessTest.java | 10 +- 26 files changed, 889 insertions(+), 157 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CompositeIdentifierMapping.java delete 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/NonAggregatedIdentifierMappingImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/CompositeSqmPathSource.java create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/aggregated/SmokeTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/nonaggregated/SmokeTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/package-info.java diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 51794579a8..726d1f6c80 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -28,16 +28,17 @@ import org.hibernate.event.spi.LoadEventListener; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.entity.CacheEntityLoaderHelper; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.stat.spi.StatisticsImplementor; -import org.hibernate.tuple.IdentifierProperty; import org.hibernate.tuple.entity.EntityMetamodel; -import org.hibernate.type.EmbeddedComponentType; -import org.hibernate.type.EntityType; -import org.hibernate.type.Type; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * Defines the default load event listeners used by hibernate for loading entities @@ -123,51 +124,50 @@ public class DefaultLoadEventListener implements LoadEventListener { final LoadEvent event, final LoadEventListener.LoadType loadType, final Class idClass) { - // we may have the kooky jpa requirement of allowing find-by-id where - // "id" is the "simple pk value" of a dependent objects parent. This - // is part of its generally goofy "derived identity" "feature" - final IdentifierProperty identifierProperty = persister.getEntityMetamodel().getIdentifierProperty(); - if ( identifierProperty.isEmbedded() ) { - final EmbeddedComponentType dependentIdType = - (EmbeddedComponentType) identifierProperty.getType(); - if ( dependentIdType.getSubtypes().length == 1 ) { - final Type singleSubType = dependentIdType.getSubtypes()[0]; - if ( singleSubType.isEntityType() ) { - final EntityType dependentParentType = (EntityType) singleSubType; - final SessionFactoryImplementor factory = event.getSession().getFactory(); - final Type dependentParentIdType = dependentParentType.getIdentifierOrUniqueKeyType( factory ); - if ( dependentParentIdType.getReturnedClass().isInstance( event.getEntityId() ) ) { - // yep that's what we have... - loadByDerivedIdentitySimplePkValue( - event, - loadType, - persister, - dependentIdType, - factory.getMetamodel().entityPersister( dependentParentType.getAssociatedEntityName() ) - ); - return; - } + // we may have the jpa requirement of allowing find-by-id where id is the "simple pk value" of a + // dependent objects parent. This is part of its generally goofy derived identity "feature" + final EntityIdentifierMapping idMapping = persister.getIdentifierMapping(); + if ( idMapping instanceof CompositeIdentifierMapping ) { + final CompositeIdentifierMapping cidMapping = (CompositeIdentifierMapping) idMapping; + + if ( cidMapping.getAttributeCount() == 1 ) { + final AttributeMapping singleIdAttribute = cidMapping.getAttributes().iterator().next(); + if ( singleIdAttribute.getMappedTypeDescriptor() instanceof EntityMappingType ) { + final EntityMappingType dependentIdTargetMapping = (EntityMappingType) singleIdAttribute.getMappedTypeDescriptor(); + final EntityIdentifierMapping dependentIdTargetIdMapping = dependentIdTargetMapping.getIdentifierMapping(); + final JavaTypeDescriptor dependentParentIdJtd = dependentIdTargetIdMapping.getMappedTypeDescriptor().getMappedJavaTypeDescriptor(); + if ( dependentParentIdJtd.getJavaType().isInstance( event.getEntityId() ) ) { + // yep that's what we have... + loadByDerivedIdentitySimplePkValue( + event, + loadType, + persister, + (EntityPersister) dependentIdTargetMapping + ); + return; } } } - throw new TypeMismatchException( - "Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass - + ", got " + event.getEntityId().getClass() - ); + } + + throw new TypeMismatchException( + "Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass + + ", got " + event.getEntityId().getClass() + ); } private void loadByDerivedIdentitySimplePkValue( LoadEvent event, LoadEventListener.LoadType options, EntityPersister dependentPersister, - EmbeddedComponentType dependentIdType, +// EmbeddedComponentType dependentIdType, EntityPersister parentPersister) { final EventSource session = event.getSession(); final EntityKey parentEntityKey = session.generateEntityKey( event.getEntityId(), parentPersister ); final Object parent = doLoad( event, parentPersister, parentEntityKey, options ); - final Serializable dependent = (Serializable) dependentIdType.instantiate( parent, session ); - dependentIdType.setPropertyValues( dependent, new Object[] {parent}, dependentPersister.getEntityMode() ); + final Serializable dependent = (Serializable) dependentPersister.instantiate( parent, session ); + dependentPersister.setPropertyValues( dependent, new Object[] {parent} ); final EntityKey dependentEntityKey = session.generateEntityKey( dependent, dependentPersister ); event.setEntityId( dependent ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/DynamicMapInstantiator.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/DynamicMapInstantiator.java index b1e1441748..fc91654c6a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/DynamicMapInstantiator.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/DynamicMapInstantiator.java @@ -15,7 +15,6 @@ import java.util.Set; import org.hibernate.EntityNameResolver; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.metamodel.spi.Instantiator; @@ -58,7 +57,7 @@ public class DynamicMapInstantiator implements Instantiator { } @Override - public Map instantiate(SharedSessionContractImplementor session) { + public Map instantiate(SessionFactoryImplementor sessionFactory) { Map map = generateMap(); if ( roleName != null ) { //noinspection unchecked diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/OptimizedPojoInstantiatorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/OptimizedPojoInstantiatorImpl.java index 487a3f9921..5a2218f5d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/OptimizedPojoInstantiatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/OptimizedPojoInstantiatorImpl.java @@ -7,7 +7,7 @@ package org.hibernate.metamodel.internal; import org.hibernate.bytecode.spi.ReflectionOptimizer; -import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** @@ -22,7 +22,7 @@ public class OptimizedPojoInstantiatorImpl extends AbstractPojoInstantiator { } @Override - public Object instantiate(SharedSessionContractImplementor session) { + public Object instantiate(SessionFactoryImplementor sessionFactory) { return instantiationOptimizer.newInstance(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PojoInstantiatorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PojoInstantiatorImpl.java index d19c6b7493..179e51a3a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PojoInstantiatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PojoInstantiatorImpl.java @@ -10,7 +10,7 @@ import java.lang.reflect.Constructor; import org.hibernate.InstantiationException; import org.hibernate.PropertyNotFoundException; -import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; @@ -48,7 +48,7 @@ public class PojoInstantiatorImpl extends AbstractPojoInstantiator { @Override @SuppressWarnings("unchecked") - public J instantiate(SharedSessionContractImplementor session) { + public J instantiate(SessionFactoryImplementor sessionFactory) { if ( isAbstract() ) { throw new InstantiationException( "Cannot instantiate abstract class or interface: ", getMappedPojoClass() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java index 94b0d82fee..5b75c6d00d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java @@ -6,8 +6,10 @@ */ package org.hibernate.metamodel.mapping; +import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; + /** * @author Steve Ebersole */ -public interface BasicEntityIdentifierMapping extends EntityIdentifierMapping, BasicValuedModelPart { +public interface BasicEntityIdentifierMapping extends SingleAttributeIdentifierMapping, BasicValuedModelPart { } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CompositeIdentifierMapping.java new file mode 100644 index 0000000000..60c7df5bb6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CompositeIdentifierMapping.java @@ -0,0 +1,26 @@ +/* + * 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 java.util.Collection; + +/** + * Support for composite identifier mappings + * + * @author Andrea Boriero + */ +public interface CompositeIdentifierMapping extends EntityIdentifierMapping { + /** + * The number of attributes associated with this composite + */ + int getAttributeCount(); + + /** + * The attributes associated with this composite + */ + Collection getAttributes(); +} 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 deleted file mode 100644 index 64b7addc97..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddedIdentifierMapping.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.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/EntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityIdentifierMapping.java index 43cb6cea99..f6374f4dea 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityIdentifierMapping.java @@ -6,6 +6,7 @@ */ package org.hibernate.metamodel.mapping; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.property.access.spi.PropertyAccess; /** @@ -14,7 +15,9 @@ import org.hibernate.property.access.spi.PropertyAccess; public interface EntityIdentifierMapping extends ValueMapping, ModelPart { String ROLE_LOCAL_NAME = "{id}"; - PropertyAccess getPropertyAccess(); + Object getIdentifier(Object entity, SharedSessionContractImplementor session); + void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session); + Object instantiate(); @Override default String getPartName() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java index 694f4dd7eb..276660bbc3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java @@ -15,6 +15,7 @@ import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.loader.ast.spi.Loadable; +import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -47,6 +48,10 @@ public interface EntityMappingType extends ManagedMappingType, Loadable { */ EntityPersister getEntityPersister(); + default EntityRepresentationStrategy getRepresentationStrategy() { + return getEntityPersister().getRepresentationStrategy(); + } + String getEntityName(); @Override 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 index d305cd272f..903fa048c2 100644 --- 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 @@ -7,17 +7,19 @@ package org.hibernate.metamodel.mapping.internal; import java.util.Arrays; +import java.util.Collection; 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.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.ColumnConsumer; +import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; 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.MappingType; @@ -51,35 +53,43 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; /** + * Support for {@link javax.persistence.EmbeddedId} + * * @author Andrea Boriero */ -public class EmbeddedIdentifierMappingImpl - implements EmbeddedIdentifierMapping, EmbeddableValuedFetchable { +public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping, SingleAttributeIdentifierMapping, EmbeddableValuedFetchable { + private final EntityMappingType entityMapping; private final String name; private final MappingType type; private final StateArrayContributorMetadataAccess attributeMetadataAccess; private final PropertyAccess propertyAccess; private final String tableExpression; private final String[] attrColumnNames; + private final SessionFactoryImplementor sessionFactory; + @SuppressWarnings("WeakerAccess") public EmbeddedIdentifierMappingImpl( + EntityMappingType entityMapping, String name, MappingType type, StateArrayContributorMetadataAccess attributeMetadataAccess, PropertyAccess propertyAccess, String tableExpression, - String[] attrColumnNames) { + String[] attrColumnNames, + SessionFactoryImplementor sessionFactory) { + this.entityMapping = entityMapping; this.name = name; this.type = type; this.attributeMetadataAccess = attributeMetadataAccess; this.propertyAccess = propertyAccess; this.tableExpression = tableExpression; this.attrColumnNames = attrColumnNames; + this.sessionFactory = sessionFactory; } @Override - public MappingType getPartMappingType() { - return type; + public EmbeddableMappingType getPartMappingType() { + return (EmbeddableMappingType) type; } @Override @@ -93,8 +103,18 @@ public class EmbeddedIdentifierMappingImpl } @Override - public PropertyAccess getPropertyAccess() { - return propertyAccess; + public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { + return propertyAccess.getGetter().get( entity ); + } + + @Override + public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) { + propertyAccess.getSetter().set( entity, id, session.getFactory() ); + } + + @Override + public Object instantiate() { + return entityMapping.getRepresentationStrategy().getInstantiator().instantiate( sessionFactory ); } @Override @@ -271,4 +291,20 @@ public class EmbeddedIdentifierMappingImpl public void visitColumns(ColumnConsumer consumer) { getEmbeddableTypeDescriptor().visitColumns( consumer ); } + + @Override + public int getAttributeCount() { + return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(); + } + + @Override + public Collection getAttributes() { + //noinspection unchecked + return (Collection) getEmbeddableTypeDescriptor().getAttributeMappings(); + } + + @Override + public PropertyAccess getPropertyAccess() { + return propertyAccess; + } } 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 c3940cb07b..e3b645099c 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 @@ -53,6 +53,7 @@ import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.ColumnConsumer; +import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; @@ -61,6 +62,7 @@ import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; @@ -134,6 +136,23 @@ public class MappingModelCreationHelper { return propertyAccess; } + @Override + public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { + return propertyAccess.getGetter().get( entity ); + } + + @Override + public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) { + propertyAccess.getSetter().set( entity, id, session.getFactory() ); + } + + @Override + public Object instantiate() { + return entityPersister.getRepresentationStrategy() + .getInstantiator() + .instantiate( creationProcess.getCreationContext().getSessionFactory() ); + } + @Override public MappingType getPartMappingType() { return getBasicType(); @@ -318,12 +337,14 @@ public class MappingModelCreationHelper { (Component) bootProperty.getValue(), cidType, attributeMappingType -> new EmbeddedIdentifierMappingImpl( + entityPersister, attributeName, attributeMappingType, attributeMetadataAccess, propertyAccess, rootTableName, - rootTableKeyColumnNames + rootTableKeyColumnNames, + creationProcess.getCreationContext().getSessionFactory() ), creationProcess ); @@ -332,69 +353,22 @@ public class MappingModelCreationHelper { return (EmbeddedIdentifierMappingImpl) embeddableMappingType.getEmbeddedValueMapping(); } - public static EntityIdentifierMapping buildNonEncapsulatedCompositeIdentifierMapping( + public static CompositeIdentifierMapping buildNonEncapsulatedCompositeIdentifierMapping( EntityPersister entityPersister, - String rootTableName, - String[] rootTableKeyColumnNames, + List idAttributeMappings, CompositeType cidType, + PersistentClass bootEntityDescriptor, MappingModelCreationProcess creationProcess) { - final PersistentClass bootEntityDescriptor = creationProcess.getCreationContext() - .getBootModel() - .getEntityBinding( entityPersister.getEntityName() ); - final PropertyAccess propertyAccess = entityPersister.getRepresentationStrategy() - .resolvePropertyAccess( bootEntityDescriptor.getIdentifierProperty() ); + final Component bootCompositeDescriptor = (Component) bootEntityDescriptor.getIdentifier(); - return new EntityIdentifierMapping() { - - @Override - public MappingType getPartMappingType() { - // non-encapsulated means that the id attributes are directly defined on the entity - // - alternatively we could have the type here be the IdClass descriptor - return entityPersister; - } - - @Override - public PropertyAccess getPropertyAccess() { - return propertyAccess; - } - - @Override - public MappingType getMappedTypeDescriptor() { - return entityPersister; - } - - @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 new NonAggregatedIdentifierMappingImpl( + entityPersister, + idAttributeMappings, + bootCompositeDescriptor, + cidType, + creationProcess + ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java new file mode 100644 index 0000000000..4a5378aaee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java @@ -0,0 +1,111 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.HibernateException; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Property; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.SingularAttributeMapping; +import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; +import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl; +import org.hibernate.type.CompositeType; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * A "non-aggregated" composite identifier. + * + * This is an identifier mapped using JPA's {@link javax.persistence.MapsId} feature. + * + * @apiNote Technically a MapsId id does not have to be composite; we still handle that this class however + * + * @author Steve Ebersole + */ +public class NonAggregatedIdentifierMappingImpl implements CompositeIdentifierMapping { + private final EntityMappingType entityMapping; + + private final List idAttributeMappings; + + public NonAggregatedIdentifierMappingImpl( + EntityMappingType entityMapping, + List idAttributeMappings, + Component bootIdDescriptor, + CompositeType cidType, + MappingModelCreationProcess creationProcess) { + // todo (6.0) : handle MapsId and IdClass + // todo (6.0) : implement SQL AST apis (DomainResult, e.g.) + this.entityMapping = entityMapping; + this.idAttributeMappings = idAttributeMappings; + } + + @Override + public EntityMappingType getPartMappingType() { + return entityMapping; + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return entityMapping.getJavaTypeDescriptor(); + } + + @Override + public EntityMappingType getMappedTypeDescriptor() { + return entityMapping; + } + + @Override + public int getAttributeCount() { + return idAttributeMappings.size(); + } + + @Override + public Collection getAttributes() { + return idAttributeMappings; + } + + @Override + public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { + return entity; + } + + @Override + public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) { + // nothing to do + } + + @Override + public Object instantiate() { + return entityMapping; + } + + @Override + public DomainResult createDomainResult( + NavigablePath navigablePath, + TableGroup tableGroup, + String resultVariable, + DomainResultCreationState creationState) { + // we will need a specialized impl for this + throw new NotYetImplementedFor6Exception( getClass() ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java new file mode 100644 index 0000000000..862cdea874 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java @@ -0,0 +1,20 @@ +/* + * 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 org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.property.access.spi.PropertyAccess; + +/** + * @author Steve Ebersole + */ +public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping { + /** + * Access to the identifier attribute's PropertyAccess + */ + PropertyAccess getPropertyAccess(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java index 3daec10a20..8e653e9f46 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java @@ -14,11 +14,11 @@ import javax.persistence.metamodel.Bindable; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.SingularAttribute; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.model.domain.internal.AttributeContainer; import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; +import org.hibernate.metamodel.model.domain.internal.NonAggregatedCompositeSqmPathSource; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; @@ -385,7 +385,11 @@ public abstract class AbstractIdentifiableType } else if ( idClassAttributes != null && ! idClassAttributes.isEmpty() ) { // non-aggregate composite id - throw new NotYetImplementedFor6Exception( getClass() ); + return new NonAggregatedCompositeSqmPathSource( + EntityIdentifierMapping.ROLE_LOCAL_NAME, + Bindable.BindableType.SINGULAR_ATTRIBUTE, + this + ); } else { if ( isIdMappingRequired() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/CompositeSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/CompositeSqmPathSource.java new file mode 100644 index 0000000000..96472f1be8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/CompositeSqmPathSource.java @@ -0,0 +1,15 @@ +/* + * 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.model.domain.internal; + +import org.hibernate.query.sqm.SqmPathSource; + +/** + * @author Steve Ebersole + */ +public interface CompositeSqmPathSource extends SqmPathSource { +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedSqmPathSource.java index 0b49c69938..8bd179420a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedSqmPathSource.java @@ -16,7 +16,10 @@ import org.hibernate.query.sqm.tree.domain.SqmPath; /** * @author Steve Ebersole */ -public class EmbeddedSqmPathSource extends AbstractSqmPathSource implements AllowableParameterType { +public class EmbeddedSqmPathSource + extends AbstractSqmPathSource + implements CompositeSqmPathSource, AllowableParameterType { + public EmbeddedSqmPathSource( String localPathName, EmbeddableDomainType domainType, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 67b0ae2f51..ad4a320cee 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -697,7 +697,7 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen return (BasicType) sqmExpressable; } - if ( sqmExpressable instanceof EmbeddedSqmPathSource ) { + if ( sqmExpressable instanceof CompositeSqmPathSource ) { throw new NotYetImplementedFor6Exception( "Resolution of embedded-valued SqmExpressable nodes not yet implemented" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java new file mode 100644 index 0000000000..6c9f038187 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java @@ -0,0 +1,44 @@ +/* + * 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.model.domain.internal; + +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.metamodel.model.domain.ManagedDomainType; +import org.hibernate.query.hql.spi.SqmCreationState; +import org.hibernate.query.sqm.IllegalPathUsageException; +import org.hibernate.query.sqm.SqmPathSource; +import org.hibernate.query.sqm.tree.domain.SqmPath; + +/** + * Support for non-aggregated composite values + * + * @author Steve Ebersole + */ +public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource implements CompositeSqmPathSource { + public NonAggregatedCompositeSqmPathSource( + String localName, + BindableType bindableType, + ManagedDomainType container) { + super( localName, container, bindableType ); + } + + @Override + public ManagedDomainType getSqmPathType() { + return (ManagedDomainType) super.getSqmPathType(); + } + + @Override + public SqmPathSource findSubPathSource(String name) throws IllegalPathUsageException { + return (SqmPathSource) getSqmPathType().findAttribute( name ); + } + + @Override + public SqmPath createSqmPath(SqmPath lhs, SqmCreationState creationState) { + // todo (6.0) : I think this will require a specialized SqmPath as well... + throw new NotYetImplementedFor6Exception( getClass() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/Instantiator.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/Instantiator.java index db691b7940..46ebd32e0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/Instantiator.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/Instantiator.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.spi; import org.hibernate.Incubating; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; /** * Strategy for instantiating representation structure instances. @@ -20,7 +19,7 @@ public interface Instantiator { /** * Create an instance of the managed embedded value structure. */ - J instantiate(SharedSessionContractImplementor session); + J instantiate(SessionFactoryImplementor sessionFactory); /** * Performs and "instance of" check to see if the given object is an 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 39d90d8109..1390e3e238 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 @@ -75,6 +75,7 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntryFactory; @@ -200,6 +201,7 @@ import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; +import org.hibernate.type.AnyType; import org.hibernate.type.AssociationType; import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; @@ -5200,12 +5202,12 @@ public abstract class AbstractEntityPersister @Override public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { - return identifierMapping.getPropertyAccess().getGetter().get( entity ); + return identifierMapping.getIdentifier( entity, session ); } @Override public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) { - identifierMapping.getPropertyAccess().getSetter().set( entity, id, factory ); + identifierMapping.setIdentifier( entity, id, session ); } @Override @@ -5220,9 +5222,8 @@ public abstract class AbstractEntityPersister @Override public Object instantiate(Object id, SharedSessionContractImplementor session) { - Object instance = getRepresentationStrategy().getInstantiator().instantiate( session ); - identifierMapping.getPropertyAccess().getSetter().set( instance, id, factory ); - + final Object instance = getRepresentationStrategy().getInstantiator().instantiate( session.getFactory() ); + setIdentifier( instance, id, session ); return instance; } @@ -5701,7 +5702,7 @@ public abstract class AbstractEntityPersister identifierMapping = creationProcess.processSubPart( EntityIdentifierMapping.ROLE_LOCAL_NAME, (role, creationProcess1) -> - generateIdentifierMapping( creationProcess, bootEntityDescriptor.getIdentifierProperty() ) + generateIdentifierMapping( creationProcess, bootEntityDescriptor ) ); if ( getVersionType() == null ) { @@ -5908,16 +5909,23 @@ public abstract class AbstractEntityPersister } - private EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, Property identifierProperty) { + private EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) { final Type idType = getIdentifierType(); if ( idType instanceof CompositeType ) { final CompositeType cidType = (CompositeType) idType; - if ( ! cidType.isEmbedded() ) { + + // NOTE: the term `isEmbedded` here uses Hibernate's older (pre-JPA) naming for its "non-aggregated" + // composite-id support. It unfortunately conflicts with the JPA usage of "embedded". Here we normalize + // the legacy naming to the more descriptive encapsulated versus non-encapsulated phrasing + + final boolean encapsulated = ! cidType.isEmbedded(); + if ( encapsulated ) { + // we have an `@EmbeddedId` return MappingModelCreationHelper.buildEncapsulatedCompositeIdentifierMapping( this, - identifierProperty, - identifierProperty.getName(), + bootEntityDescriptor.getIdentifierProperty(), + bootEntityDescriptor.getIdentifierProperty().getName(), getRootTableName(), rootTableKeyColumnNames, cidType, @@ -5925,13 +5933,8 @@ public abstract class AbstractEntityPersister ); } - return MappingModelCreationHelper.buildNonEncapsulatedCompositeIdentifierMapping( - this, - getRootTableName(), - rootTableKeyColumnNames, - cidType, - creationProcess - ); + // otherwise we have a non-encapsulated composite-identifier + return generateNonEncapsulatedCompositeIdentifierMapping( creationProcess, bootEntityDescriptor, cidType ); } return MappingModelCreationHelper.buildSimpleIdentifierMapping( @@ -5943,6 +5946,89 @@ public abstract class AbstractEntityPersister ); } + private EntityIdentifierMapping generateNonEncapsulatedCompositeIdentifierMapping( + MappingModelCreationProcess creationProcess, + PersistentClass bootEntityDescriptor, CompositeType cidType) { + // process all of the defined "id attributes" because they are declared on the entity + final Component bootIdDescriptor = (Component) bootEntityDescriptor.getIdentifier(); + final List idAttributeMappings = new ArrayList<>( bootIdDescriptor.getPropertySpan() ); + int columnsConsumedSoFar = 0; + + if ( attributeMappings == null ) { + attributeMappings = new ArrayList<>( + bootEntityDescriptor.getPropertyClosureSpan() + bootIdDescriptor.getPropertySpan() + ); + + final Iterator bootPropertyIterator = bootIdDescriptor.getPropertyIterator(); + while ( bootPropertyIterator.hasNext() ) { + final Property bootIdProperty = (Property) bootPropertyIterator.next(); + final Type idPropertyType = bootIdProperty.getType(); + + if ( idPropertyType instanceof AnyType ) { + throw new HibernateException( + "AnyType property `" + getEntityName() + "#" + bootIdProperty.getName() + + "` cannot be used as part of entity identifier " + ); + } + + if ( idPropertyType instanceof CollectionType ) { + throw new HibernateException( + "Plural property `" + getEntityName() + "#" + bootIdProperty.getName() + + "` cannot be used as part of entity identifier " + ); + } + + final SingularAttributeMapping idAttributeMapping; + + if ( idPropertyType instanceof BasicType ) { + idAttributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping( + bootIdProperty.getName(), + attributeMappings.size(), + bootIdProperty, + this, + (BasicType) idPropertyType, + getRootTableName(), + rootTableKeyColumnNames[columnsConsumedSoFar], + getRepresentationStrategy().resolvePropertyAccess( bootIdProperty ), + CascadeStyles.ALL, + creationProcess + ); + columnsConsumedSoFar++; + } + else { +// final String[] unconsumedColumnNames = Arrays.copyOfRange( +// rootTableKeyColumnNames, +// columnsConsumedSoFar, +// rootTableKeyColumnNames.length +// ); + + if ( idPropertyType instanceof CompositeType ) { + // nested composite + throw new NotYetImplementedFor6Exception( getClass() ); + } + else if ( idPropertyType instanceof EntityType ) { + // key-many-to-one + throw new NotYetImplementedFor6Exception( getClass() ); + } + else { + throw new UnsupportedOperationException(); + } + } + + idAttributeMappings.add( idAttributeMapping ); + } + } + attributeMappings.addAll( idAttributeMappings ); + + return MappingModelCreationHelper.buildNonEncapsulatedCompositeIdentifierMapping( + this, + idAttributeMappings, + cidType, + bootEntityDescriptor, + creationProcess + ); + } + private static EntityVersionMapping generateVersionMapping( EntityPersister entityPersister, MappingModelCreationProcess creationProcess) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index f5791058f8..c4bbd1edfb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -25,6 +25,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource; import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.BinaryArithmeticOperator; @@ -905,7 +906,7 @@ public abstract class BaseSqmToSqlAstConverter return (BasicValuedMapping) parameterSqmType; } - if ( parameterSqmType instanceof EmbeddedSqmPathSource ) { + if ( parameterSqmType instanceof CompositeSqmPathSource ) { throw new NotYetImplementedFor6Exception( "Support for embedded-valued parameters not yet implemented" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index f5f91241e9..98174d102b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -15,13 +15,13 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.query.NavigablePath; -import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.graph.AbstractFetchParentAccess; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.Initializer; +import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; /** @@ -119,7 +119,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA compositeInstance = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor() .getRepresentationStrategy() .getInstantiator() - .instantiate( rowProcessingState.getSession() ); + .instantiate( rowProcessingState.getSession().getFactory() ); EmbeddableLoadingLogger.INSTANCE.debugf( "Created composite instance [%s] : %s", navigablePath, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/aggregated/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/aggregated/SmokeTests.java new file mode 100644 index 0000000000..4c476c51c9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/aggregated/SmokeTests.java @@ -0,0 +1,187 @@ +/* + * 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.metamodel.mapping.cid.aggregated; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +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; + +/** + * Simple tests that aggregated id mappings work at a basic level + * + * @author Steve Ebersole + */ +@ServiceRegistry +@DomainModel( + annotatedClasses = { + SmokeTests.Order.class, + SmokeTests.LineItem.class, + SmokeTests.LineItemId.class + } +) +@SessionFactory +public class SmokeTests { + @Test + public void simpleTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "select i from LineItem i" ).list(); + } + ); + } + + @BeforeEach + public void createTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final Order order = new Order( 1, "123-abc" ); + session.persist( order ); + + session.persist( new LineItem( order, 1, "xyz", 500 ) ); + session.persist( new LineItem( order, 2, "tuv", 60 ) ); + session.persist( new LineItem( order, 3, "def", 350 ) ); + } + ); + } + + @AfterEach + public void cleanUpTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete LineItem" ).executeUpdate(); + session.createQuery( "delete Order" ).executeUpdate(); + } + ); + } + + @Entity( name = "Order" ) + @Table( name = "orders" ) + public static class Order { + private Integer id; + private String invoice; + + public Order() { + } + + public Order(Integer id, String invoice) { + this.id = id; + this.invoice = invoice; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getInvoice() { + return invoice; + } + + public void setInvoice(String invoice) { + this.invoice = invoice; + } + } + + @Embeddable + public static class LineItemId implements Serializable { + private Order order; + private Integer lineNumber; + + public LineItemId() { + } + + public LineItemId(Order order, Integer lineNumber) { + this.order = order; + this.lineNumber = lineNumber; + } + + @ManyToOne + @JoinColumn( name = "order_id" ) + public Order getOrder() { + return order; + } + + public void setOrder(Order order) { + this.order = order; + } + + @Column( name = "line_number" ) + public Integer getLineNumber() { + return lineNumber; + } + + public void setLineNumber(Integer lineNumber) { + this.lineNumber = lineNumber; + } + } + + @Entity( name = "LineItem" ) + @Table( name = "line_items" ) + public static class LineItem { + private LineItemId id; + private String sku; + private int quantity; + + public LineItem() { + } + + public LineItem(LineItemId id, String sku, int quantity) { + this.id = id; + this.sku = sku; + this.quantity = quantity; + } + + public LineItem(Order order, int lineNumber, String sku, int quantity) { + this.id = new LineItemId( order, lineNumber ); + this.sku = sku; + this.quantity = quantity; + } + + @EmbeddedId + public LineItemId getId() { + return id; + } + + public void setId(LineItemId id) { + this.id = id; + } + + public String getSku() { + return sku; + } + + public void setSku(String sku) { + this.sku = sku; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/nonaggregated/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/nonaggregated/SmokeTests.java new file mode 100644 index 0000000000..4c04ffd9a2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/nonaggregated/SmokeTests.java @@ -0,0 +1,222 @@ +/* + * 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.metamodel.mapping.cid.nonaggregated; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.engine.spi.SessionFactoryImplementor; + +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.transaction.TransactionUtil2; +import org.junit.jupiter.api.Test; + +/** + * Simple tests that aggregated id mappings work at a basic level + * + * @author Steve Ebersole + */ +@SuppressWarnings("WeakerAccess") +public class SmokeTests { + @Test + @FailureExpected( reason = "See org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl#createDomainResult" ) + public void simpleTest() { + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); + try { + final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) new MetadataSources( ssr ) + .addAnnotatedClass( SystemAccess.class ) + .buildMetadata() + .buildSessionFactory(); + TransactionUtil2.inTransaction( + sessionFactory, + session -> { + session.createQuery( "select a from SystemAccess a" ).list(); + } + ); + } + finally { + StandardServiceRegistryBuilder.destroy( ssr ); + } + } + + @Test + @FailureExpected( reason = "Support for non-aggregated composite-ids not yet fully implemented" ) + public void keyManyToOneTest() { + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); + + try { + final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) new MetadataSources( ssr ) + .addAnnotatedClass( Order.class ) + .addAnnotatedClass( LineItem.class ) + .buildMetadata() + .buildSessionFactory(); + TransactionUtil2.inTransaction( + sessionFactory, + session -> { + session.createQuery( "select i from LineItem i" ).list(); + } + ); + } + finally { + StandardServiceRegistryBuilder.destroy( ssr ); + } + } + +// @BeforeEach +// public void createTestData(SessionFactoryScope scope) { +// scope.inTransaction( +// session -> { +// final Order order = new Order( 1, "123-abc" ); +// session.persist( order ); +// +// session.persist( new LineItem( order, 1, "xyz", 500 ) ); +// session.persist( new LineItem( order, 2, "tuv", 60 ) ); +// session.persist( new LineItem( order, 3, "def", 350 ) ); +// } +// ); +// } +// +// @AfterEach +// public void cleanUpTestData(SessionFactoryScope scope) { +// scope.inTransaction( +// session -> { +// session.createQuery( "delete LineItem" ).executeUpdate(); +// session.createQuery( "delete Order" ).executeUpdate(); +// } +// ); +// } + + @Entity( name = "SystemAccess" ) + @Table( name = "`access`" ) + public static class SystemAccess implements Serializable { + private String system; + private String userId; + private String accessCode; + + @Id + public String getSystem() { + return system; + } + + public void setSystem(String system) { + this.system = system; + } + + @Id + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getAccessCode() { + return accessCode; + } + + public void setAccessCode(String accessCode) { + this.accessCode = accessCode; + } + } + + @Entity( name = "Order" ) + @Table( name = "orders" ) + public static class Order { + private Integer id; + private String invoice; + + public Order() { + } + + public Order(Integer id, String invoice) { + this.id = id; + this.invoice = invoice; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getInvoice() { + return invoice; + } + + public void setInvoice(String invoice) { + this.invoice = invoice; + } + } + + @Entity( name = "LineItem" ) + @Table( name = "line_items" ) + public static class LineItem implements Serializable { + private Order order; + private Integer lineNumber; + private String sku; + private int quantity; + + public LineItem() { + } + + public LineItem(Order order, int lineNumber, String sku, int quantity) { + this.order = order; + this.lineNumber = lineNumber; + this.sku = sku; + this.quantity = quantity; + } + + @Id + @ManyToOne + @JoinColumn( name = "order_id" ) + public Order getOrder() { + return order; + } + + public void setOrder(Order order) { + this.order = order; + } + + @Id + @Column( name = "line_number" ) + public Integer getLineNumber() { + return lineNumber; + } + + public void setLineNumber(Integer lineNumber) { + this.lineNumber = lineNumber; + } + + public String getSku() { + return sku; + } + + public void setSku(String sku) { + this.sku = sku; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/package-info.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/package-info.java new file mode 100644 index 0000000000..c1b58fcc7c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/cid/package-info.java @@ -0,0 +1,11 @@ +/* + * 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 + */ + +/** + * Testing support of composite identifier mappings + */ +package org.hibernate.orm.test.metamodel.mapping.cid; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/access/xml/XmlAccessTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/access/xml/XmlAccessTest.java index 2d8a058433..cb878b61be 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/access/xml/XmlAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/access/xml/XmlAccessTest.java @@ -15,10 +15,11 @@ import javax.persistence.AccessType; import org.hibernate.cfg.Configuration; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.GetterFieldImpl; import org.hibernate.property.access.spi.GetterMethodImpl; -import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Assert; @@ -179,10 +180,9 @@ public class XmlAccessTest extends BaseUnitTestCase { // uses the first getter of the tupelizer for the assertions private void assertAccessType(SessionFactoryImplementor factory, Class classUnderTest, AccessType accessType) { - final Getter idGetter = factory.getDomainModel().findEntityDescriptor( classUnderTest.getName() ) - .getIdentifierMapping() - .getPropertyAccess() - .getGetter(); + final EntityPersister entityDescriptor = factory.getDomainModel().findEntityDescriptor( classUnderTest.getName() ); + final SingleAttributeIdentifierMapping identifierMapping = (SingleAttributeIdentifierMapping) entityDescriptor.getIdentifierMapping(); + final Getter idGetter = identifierMapping.getPropertyAccess().getGetter(); if ( AccessType.FIELD.equals( accessType ) ) { Assert.assertTrue(