Introduce `VirtualIdEmbeddable` and `IdClassEmbeddable`

EmbeddableInstantiator

Still need to
  - integrate EmbeddableInstantiator work
  - integrate embedded forms.  `VirtualIdEmbeddable` does not really need it as it can use the id-mapping itself as the embedded form.  But `IdClassEmbedded` should really be integrated
  - integrate `VirtualKeyEmbeddable` and `VirtualKeyEmbedded` for use as inverse composite fks
  - share `#finishInit` handling for `EmbeddableMappingType`, `VirtualIdEmbeddable` and `IdClassEmbeddable`
This commit is contained in:
Steve Ebersole 2021-11-21 09:21:59 -06:00
parent 546a635be9
commit 0322d8fa84
12 changed files with 98 additions and 105 deletions

View File

@ -8,64 +8,45 @@ package org.hibernate.bytecode.spi;
/**
* Represents reflection optimization for a particular class.
*
* @author Steve Ebersole
*/
public interface ReflectionOptimizer {
/**
* Retrieve the optimizer for calling an entity's constructor via reflection.
*
* @return The optimizer for instantiation
*/
public InstantiationOptimizer getInstantiationOptimizer();
InstantiationOptimizer getInstantiationOptimizer();
/**
* Retrieve the optimizer for accessing the entity's persistent state.
*
* @return The optimizer for persistent state access
*/
public AccessOptimizer getAccessOptimizer();
AccessOptimizer getAccessOptimizer();
/**
* Represents optimized entity instantiation.
*/
public static interface InstantiationOptimizer {
interface InstantiationOptimizer {
/**
* Perform instantiation of an instance of the underlying class.
*
* @return The new instance.
*/
public Object newInstance();
Object newInstance();
}
/**
* Represents optimized entity property access.
*
* @author Steve Ebersole
*/
public interface AccessOptimizer {
interface AccessOptimizer {
/**
* Get the name of all properties.
*
* @return The name of all properties.
*/
public String[] getPropertyNames();
String[] getPropertyNames();
/**
* Get the value of all properties from the given entity
*
* @param object The entity from which to extract values.
*
* @return The values.
*/
public Object[] getPropertyValues(Object object);
Object[] getPropertyValues(Object object);
/**
* Set all property values into an entity instance.
*
* @param object The entity instance
* @param values The values to inject
*/
public void setPropertyValues(Object object, Object[] values);
void setPropertyValues(Object object, Object[] values);
}
}

View File

@ -7,12 +7,15 @@
package org.hibernate.loader;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.query.NavigablePath;
/**
* @author Steve Ebersole
*/
public class PropertyPath {
public static final String IDENTIFIER_MAPPER_PROPERTY = "_identifierMapper";
public static final String IDENTIFIER_MAPPER_PROPERTY = NavigablePath.IDENTIFIER_MAPPER_PROPERTY;
private final PropertyPath parent;
private final String property;
private final String fullPath;

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.internal;
import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.type.descriptor.java.JavaType;
@ -17,21 +18,25 @@ import static org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimi
/**
* Support for instantiating embeddables as POJO representation
* using bytecode optimizer
*
* @author Steve Ebersole
*/
public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiator implements EmbeddableInstantiator {
private final Supplier<EmbeddableMappingType> embeddableMappingAccess;
private final InstantiationOptimizer instantiationOptimizer;
public EmbeddableInstantiatorPojoOptimized(
JavaType<?> javaTypeDescriptor,
Supplier<EmbeddableMappingType> embeddableMappingAccess,
InstantiationOptimizer instantiationOptimizer) {
super( javaTypeDescriptor.getJavaTypeClass() );
this.embeddableMappingAccess = embeddableMappingAccess;
this.instantiationOptimizer = instantiationOptimizer;
}
@Override
public Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory) {
return instantiationOptimizer.newInstance();
final Object embeddable = instantiationOptimizer.newInstance();
final EmbeddableMappingType embeddableMapping = embeddableMappingAccess.get();
embeddableMapping.setPropertyValues( embeddable, valuesAccess.get() );
return embeddable;
}
}

View File

@ -10,7 +10,6 @@ import java.lang.reflect.Constructor;
import java.util.function.Supplier;
import org.hibernate.InstantiationException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
@ -22,18 +21,15 @@ import org.hibernate.type.descriptor.java.JavaType;
/**
* Support for instantiating embeddables as POJO representation
*
* @author Steve Ebersole
*/
public class EmbeddableInstantiatorPojoStandard extends AbstractPojoInstantiator implements EmbeddableInstantiator {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( PojoInstantiatorImpl.class );
private final Supplier<EmbeddableMappingType> embeddableMappingAccess;
private final boolean constructorInjection = false;
private final Constructor<?> constructor;
public EmbeddableInstantiatorPojoStandard(
Supplier<EmbeddableMappingType> embeddableMappingAccess,
JavaType<?> javaTypeDescriptor) {
public EmbeddableInstantiatorPojoStandard(JavaType<?> javaTypeDescriptor, Supplier<EmbeddableMappingType> embeddableMappingAccess) {
super( javaTypeDescriptor.getJavaTypeClass() );
this.embeddableMappingAccess = embeddableMappingAccess;
@ -54,29 +50,29 @@ public class EmbeddableInstantiatorPojoStandard extends AbstractPojoInstantiator
@Override
public Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory) {
if ( isAbstract() ) {
throw new InstantiationException( "Cannot instantiate abstract class or interface: ", getMappedPojoClass() );
throw new InstantiationException(
"Cannot instantiate abstract class or interface: ", getMappedPojoClass()
);
}
if ( constructor == null ) {
throw new InstantiationException( "No default constructor for embeddable: ", getMappedPojoClass() );
}
if ( valuesAccess != null ) {
if ( constructor.getParameterTypes().length > 0 ) {
// constructor injection
throw new NotYetImplementedFor6Exception( "Constructor injection for embeddables not yet implemented" );
}
throw new InstantiationException( "Unable to locate constructor for embeddable", getMappedPojoClass() );
}
try {
if ( constructorInjection ) {
return constructor.newInstance( valuesAccess.get() );
}
final Object instance = constructor.newInstance();
if ( valuesAccess != null ) {
embeddableMappingAccess.get().setPropertyValues( instance, valuesAccess.get() );
}
return instance;
}
catch ( Exception e ) {
throw new InstantiationException( "Could not instantiate embeddable: ", getMappedPojoClass(), e );
throw new InstantiationException( "Could not instantiate entity: ", getMappedPojoClass(), e );
}
}
}

View File

@ -75,14 +75,12 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
final ReflectionOptimizer.InstantiationOptimizer instantiationOptimizer = reflectionOptimizer.getInstantiationOptimizer();
return new EmbeddableInstantiatorPojoOptimized(
getEmbeddableJavaTypeDescriptor(),
runtimeDescriptorAccess,
instantiationOptimizer
);
}
return new EmbeddableInstantiatorPojoStandard(
runtimeDescriptorAccess,
getEmbeddableJavaTypeDescriptor()
);
return new EmbeddableInstantiatorPojoStandard( getEmbeddableJavaTypeDescriptor(), runtimeDescriptorAccess );
}
@Override

View File

@ -43,6 +43,7 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping.IdentifierValueMapper;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
@ -50,7 +51,6 @@ import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping.IdentifierValueMapper;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.persister.entity.EntityPersister;

View File

@ -17,7 +17,7 @@ import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.type.descriptor.java.JavaType;
/**
* @author Steve Ebersole
* EmbeddableRepresentationStrategy for an IdClass mapping
*/
public class IdClassRepresentationStrategy implements EmbeddableRepresentationStrategy {
private final JavaType<?> idClassType;
@ -25,7 +25,7 @@ public class IdClassRepresentationStrategy implements EmbeddableRepresentationSt
public IdClassRepresentationStrategy(IdClassEmbeddable idClassEmbeddable) {
this.idClassType = idClassEmbeddable.getMappedJavaTypeDescriptor();
this.instantiator = new EmbeddableInstantiatorPojoStandard( () -> idClassEmbeddable, idClassType );
this.instantiator = new EmbeddableInstantiatorPojoStandard( idClassType, () -> idClassEmbeddable );
}
@Override

View File

@ -18,9 +18,9 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.RootClass;
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;

View File

@ -10,6 +10,7 @@ import java.util.Objects;
import org.hibernate.query.DotIdentifierSequence;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.query.NavigablePath;
/**
* Poorly named.
@ -21,7 +22,7 @@ import org.hibernate.internal.util.StringHelper;
* @author Steve Ebersole
*/
public class NavigableRole implements DotIdentifierSequence {
public static final String IDENTIFIER_MAPPER_PROPERTY = "_identifierMapper";
public static final String IDENTIFIER_MAPPER_PROPERTY = NavigablePath.IDENTIFIER_MAPPER_PROPERTY;
private final NavigableRole parent;
private final String localName;

View File

@ -6,18 +6,22 @@
*/
package org.hibernate.metamodel.spi;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* Contract for instantiating embeddable values
*
* @author Steve Ebersole
*/
public interface EmbeddableInstantiator extends Instantiator {
/**
* Create an instance of the embeddable
*/
Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory);
// default Object instantiate(IntFunction<Object> valueAccess, SessionFactoryImplementor sessionFactory) {
// throw new NotYetImplementedFor6Exception( getClass() );
// }
}

View File

@ -39,5 +39,7 @@ public abstract class AbstractFetchParentAccess implements FetchParentAccess {
for ( Consumer<Object> listener : listeners ) {
listener.accept( parentInstance );
}
listeners.clear();
}
}

View File

@ -6,19 +6,16 @@
*/
package org.hibernate.sql.results.graph.embeddable;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.property.access.spi.PropertyAccess;
@ -37,15 +34,20 @@ import org.hibernate.sql.results.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
/**
* @author Steve Ebersole
*/
public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess implements EmbeddableInitializer {
public static final Object NULL_MARKER = new Object();
private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart embeddedModelPartDescriptor;
private final EmbeddableValuedModelPart embedded;
private final EmbeddableRepresentationStrategy representationStrategy;
private FetchParentAccess fetchParentAccess;
private final Map<StateArrayContributorMapping, DomainResultAssembler> assemblerMap;
private final List<DomainResultAssembler<?>> assemblers;
// per-row state
private final Object[] resolvedValues;
@ -59,15 +61,25 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
FetchParentAccess fetchParentAccess,
AssemblerCreationState creationState) {
this.navigablePath = resultDescriptor.getNavigablePath();
this.embeddedModelPartDescriptor = resultDescriptor.getReferencedMappingContainer();
this.embedded = resultDescriptor.getReferencedMappingContainer();
this.fetchParentAccess = fetchParentAccess;
final EmbeddableMappingType embeddableTypeDescriptor = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor();
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
if ( embedded instanceof CompositeIdentifierMapping ) {
representationStrategy = ( (CompositeIdentifierMapping) embedded )
.getMappedIdEmbeddableTypeDescriptor()
.getRepresentationStrategy();
}
else {
representationStrategy = embeddableTypeDescriptor.getRepresentationStrategy();
}
final int numOfAttrs = embeddableTypeDescriptor.getNumberOfAttributeMappings();
this.resolvedValues = new Object[ numOfAttrs ];
this.assemblerMap = new IdentityHashMap<>( numOfAttrs );
this.assemblers = arrayList( numOfAttrs );
embeddedModelPartDescriptor.visitFetchables(
embeddableTypeDescriptor.visitFetchables(
stateArrayContributor -> {
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
@ -75,7 +87,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
? new NullValueAssembler<>( stateArrayContributor.getJavaTypeDescriptor() )
: fetch.createAssembler( this, creationState );
assemblerMap.put( (StateArrayContributorMapping) stateArrayContributor, stateAssembler );
assemblers.add( stateAssembler );
},
null
);
@ -89,7 +101,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
@Override
public EmbeddableValuedModelPart getInitializedPart() {
return embeddedModelPartDescriptor;
return embedded;
}
@SuppressWarnings("WeakerAccess")
@ -103,7 +115,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
@Override
public Object getCompositeInstance() {
return compositeInstance;
return compositeInstance == NULL_MARKER ? null : compositeInstance;
}
@Override
@ -111,9 +123,10 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
if ( compositeInstance != null ) {
return;
}
final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess();
final PropertyAccess parentInjectionPropertyAccess = embedded.getParentInjectionAttributePropertyAccess();
final FetchParentAccess fetchParentAccess = getFetchParentAccess();
if ( parentInjectionPropertyAccess != null && fetchParentAccess != null ) {
fetchParentAccess.findFirstEntityDescriptorAccess().registerResolutionListener(
// todo (6.0) : this is the legacy behavior
@ -142,26 +155,20 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
// Special handling for non-aggregated attributes which use the actual entity instance as container,
// which we access through the fetch parent access.
// If this model part is an identifier, we must construct the instance as this is called during resolveKey
final EmbeddableMappingType embeddableTypeDescriptor = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor();
final EmbeddableRepresentationStrategy representationStrategy;
if ( embeddedModelPartDescriptor instanceof CompositeIdentifierMapping ) {
representationStrategy = ( (CompositeIdentifierMapping) embeddedModelPartDescriptor ).getMappedIdEmbeddableTypeDescriptor()
.getRepresentationStrategy();
}
else {
representationStrategy = embeddableTypeDescriptor.getRepresentationStrategy();
}
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
if ( fetchParentAccess != null && embeddableTypeDescriptor.getMappedJavaTypeDescriptor().getJavaTypeClass()
.isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() )
&& embeddableTypeDescriptor.getMappedJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor<?>
&& !( embeddedModelPartDescriptor instanceof CompositeIdentifierMapping )
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embeddedModelPartDescriptor.getFetchableName() ) ) {
&& !( embedded instanceof CompositeIdentifierMapping )
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) ) {
fetchParentAccess.resolveInstance( rowProcessingState );
compositeInstance = fetchParentAccess.getInitializedInstance();
}
if ( compositeInstance == null ) {
compositeInstance = representationStrategy.getInstantiator()
compositeInstance = representationStrategy
.getInstantiator()
.instantiate( null, rowProcessingState.getSession().getFactory() );
}
@ -173,10 +180,9 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
@Override
public void initializeInstance(RowProcessingState rowProcessingState) {
final PropertyAccess parentInjectionPropertyAccess = embedded.getParentInjectionAttributePropertyAccess();
final Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess();
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
if ( parentInjectionPropertyAccess != null ) {
final Object owner;
if ( initializer instanceof CollectionInitializer ) {
@ -207,16 +213,14 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
);
boolean areAllValuesNull = true;
final Set<Map.Entry<StateArrayContributorMapping, DomainResultAssembler>> entries = assemblerMap.entrySet();
final int size = entries.size();
for ( Map.Entry<StateArrayContributorMapping, DomainResultAssembler> entry : entries ) {
final DomainResultAssembler<?> assembler = entry.getValue();
for ( int i = 0; i < assemblers.size(); i++ ) {
final DomainResultAssembler<?> assembler = assemblers.get( i );
final Object contributorValue = assembler.assemble(
rowProcessingState,
rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
);
resolvedValues[entry.getKey().getStateArrayPosition()] = contributorValue;
resolvedValues[i] = contributorValue;
if ( contributorValue != null ) {
areAllValuesNull = false;
}
@ -234,8 +238,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
);
}
else {
Object target = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
Object target = representationStrategy
.getInstantiator()
.instantiate( null, rowProcessingState.getSession().getFactory() );
setPropertyValuesOnTarget( target, rowProcessingState.getSession() );
@ -258,23 +261,23 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
private void setPropertyValuesOnTarget(Object compositeInstance, SharedSessionContractImplementor session) {
final EmbeddableMappingType embeddableTypeDescriptor;
if ( embeddedModelPartDescriptor instanceof CompositeIdentifierMapping ) {
final CompositeIdentifierMapping compositeIdentifierMapping = (CompositeIdentifierMapping) this.embeddedModelPartDescriptor;
if ( embedded instanceof CompositeIdentifierMapping ) {
final CompositeIdentifierMapping compositeIdentifierMapping = (CompositeIdentifierMapping) embedded;
embeddableTypeDescriptor = compositeIdentifierMapping.getMappedIdEmbeddableTypeDescriptor();
if ( compositeIdentifierMapping.hasContainingClass() ) {
// For id-classes, we might have to transform from the virtual representation to the id-class representation
// in case the virtual representation contains a to-one attribute, that is mapped by an embeddable in the id-class
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().forEachAttributeMapping(
embedded.getEmbeddableTypeDescriptor().forEachAttributeMapping(
(index, attributeMapping) -> {
final AttributeMapping idClassAttribute = embeddableTypeDescriptor.getAttributeMappings().get( index );
if ( attributeMapping instanceof ToOneAttributeMapping && !( idClassAttribute instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
final Object associationKey = toOneAttributeMapping.getForeignKeyDescriptor()
.getAssociationKeyFromSide(
resolvedValues[index],
toOneAttributeMapping.getSideNature().inverse(),
session
);
final ForeignKeyDescriptor fkDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
resolvedValues[index],
toOneAttributeMapping.getSideNature().inverse(),
session
);
resolvedValues[index] = associationKey;
}
}
@ -282,7 +285,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
}
}
else {
embeddableTypeDescriptor = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor();
embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
}
embeddableTypeDescriptor.setPropertyValues(
compositeInstance,