Introduce `VirtualIdEmbeddable` and `IdClassEmbeddable` + instantiators

Prep work for EmbeddableInstantiator - initializer

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`
  - ability to use the containing composite owner as the parent of a composite (legacy behavior is to always use the "first" entity
This commit is contained in:
Steve Ebersole 2021-11-30 16:07:58 -06:00
parent 142299e7a8
commit 42d1fcca19
1 changed files with 55 additions and 48 deletions

View File

@ -52,6 +52,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart embedded; private final EmbeddableValuedModelPart embedded;
private final EmbeddableMappingType representationEmbeddable;
private final EmbeddableRepresentationStrategy representationStrategy; private final EmbeddableRepresentationStrategy representationStrategy;
private final FetchParentAccess fetchParentAccess; private final FetchParentAccess fetchParentAccess;
private final boolean createEmptyCompositesEnabled; private final boolean createEmptyCompositesEnabled;
@ -78,24 +79,19 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
this.fetchParentAccess = fetchParentAccess; this.fetchParentAccess = fetchParentAccess;
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor(); final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
if ( embedded instanceof CompositeIdentifierMapping ) { if ( embedded instanceof CompositeIdentifierMapping ) {
representationStrategy = ( (CompositeIdentifierMapping) embedded ) representationEmbeddable = ( (CompositeIdentifierMapping) embedded ).getMappedIdEmbeddableTypeDescriptor();
.getMappedIdEmbeddableTypeDescriptor()
.getRepresentationStrategy();
} }
else { else {
representationStrategy = embeddableTypeDescriptor.getRepresentationStrategy(); representationEmbeddable = embeddableTypeDescriptor;
} }
representationStrategy = representationEmbeddable.getRepresentationStrategy();
final int numOfAttrs = embeddableTypeDescriptor.getNumberOfAttributeMappings(); final int numOfAttrs = embeddableTypeDescriptor.getNumberOfAttributeMappings();
this.resolvedValues = new Object[ numOfAttrs ]; this.resolvedValues = new Object[ numOfAttrs ];
this.assemblers = arrayList( numOfAttrs ); this.assemblers = arrayList( numOfAttrs );
// todo (6.0) - why not just use the "maps id" form right here?
// ( (CompositeIdentifierMapping) embedded ).getMappedIdEmbeddableTypeDescriptor()
// in theory this should let us avoid the explicit maps-id handling via `setPropertyValuesOnTarget`
embeddableTypeDescriptor.visitFetchables( embeddableTypeDescriptor.visitFetchables(
stateArrayContributor -> { stateArrayContributor -> {
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor ); final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
@ -162,36 +158,25 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
prepareCompositeInstance( processingState ); prepareCompositeInstance( processingState );
handleParentInjection( processingState ); handleParentInjection( processingState );
if ( !createEmptyCompositesEnabled && allValuesNull == TRUE ) { if ( compositeInstance != NULL_MARKER ) {
compositeInstance = NULL_MARKER;
}
else {
notifyResolutionListeners( compositeInstance ); notifyResolutionListeners( compositeInstance );
if ( compositeInstance instanceof HibernateProxy ) { if ( compositeInstance instanceof HibernateProxy ) {
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() ); final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
if ( parentInitializer != this ) { if ( parentInitializer != this ) {
( (FetchParentAccess) parentInitializer ).registerResolutionListener( ( (FetchParentAccess) parentInitializer ).registerResolutionListener(
entity -> setPropertyValuesOnTarget( entity, processingState.getSession() ) (entity) -> setPropertyValuesOnTarget( entity, processingState )
); );
} }
else { else {
Object target = representationStrategy Object target = representationStrategy
.getInstantiator() .getInstantiator()
.instantiate( null, sessionFactory ); .instantiate( null, sessionFactory );
setPropertyValuesOnTarget( target, processingState.getSession() ); setPropertyValuesOnTarget( target, processingState );
( (HibernateProxy) compositeInstance ).getHibernateLazyInitializer().setImplementation( target ); ( (HibernateProxy) compositeInstance ).getHibernateLazyInitializer().setImplementation( target );
} }
} }
// At this point, createEmptyCompositesEnabled is always true.
// We can only set the property values on the compositeInstance though if there is at least one non null value.
// If the values are all null, we would normally not create a composite instance at all because no values exist.
// Setting all properties to null could cause IllegalArgumentExceptions though when the component has primitive properties.
// To avoid this exception and align with what Hibernate 5 did, we skip setting properties if all values are null.
// A possible alternative could be to initialize the resolved values for primitive fields to their default value,
// but that might cause unexpected outcomes for Hibernate 5 users that use createEmptyCompositesEnabled when updating.
// You can see the need for this by running EmptyCompositeEquivalentToNullTest
else if ( allValuesNull == FALSE ) { else if ( allValuesNull == FALSE ) {
setPropertyValuesOnTarget( compositeInstance, processingState.getSession() ); setPropertyValuesOnTarget( compositeInstance, processingState );
} }
} }
} }
@ -227,9 +212,12 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
} }
if ( compositeInstance == null ) { if ( compositeInstance == null ) {
compositeInstance = representationStrategy compositeInstance = createCompositeInstance(
.getInstantiator() navigablePath,
.instantiate( null, sessionFactory ); representationStrategy,
processingState,
sessionFactory
);
} }
EmbeddableLoadingLogger.INSTANCE.debugf( EmbeddableLoadingLogger.INSTANCE.debugf(
@ -254,6 +242,22 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
} }
} }
private Object createCompositeInstance(
NavigablePath navigablePath,
EmbeddableRepresentationStrategy representationStrategy,
RowProcessingState processingState,
SessionFactoryImplementor sessionFactory) {
if ( !createEmptyCompositesEnabled && allValuesNull == TRUE ) {
return NULL_MARKER;
}
final Object instance = representationStrategy.getInstantiator().instantiate( null, sessionFactory );
EmbeddableLoadingLogger.INSTANCE.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
return instance;
}
private void handleParentInjection(RowProcessingState processingState) { private void handleParentInjection(RowProcessingState processingState) {
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess(); final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
if ( parentInjectionAccess == null ) { if ( parentInjectionAccess == null ) {
@ -339,38 +343,41 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
private void setPropertyValuesOnTarget(Object compositeInstance, SharedSessionContractImplementor session) { private void setPropertyValuesOnTarget(Object compositeInstance, RowProcessingState processingState) {
final EmbeddableMappingType embeddableTypeDescriptor; applyMapsId( processingState );
representationEmbeddable.setPropertyValues( compositeInstance, resolvedValues );
}
private void applyMapsId(RowProcessingState processingState) {
final SharedSessionContractImplementor session = processingState.getSession();
if ( embedded instanceof CompositeIdentifierMapping ) { if ( embedded instanceof CompositeIdentifierMapping ) {
final CompositeIdentifierMapping compositeIdentifierMapping = (CompositeIdentifierMapping) embedded; final CompositeIdentifierMapping cid = (CompositeIdentifierMapping) embedded;
embeddableTypeDescriptor = compositeIdentifierMapping.getMappedIdEmbeddableTypeDescriptor(); final EmbeddableMappingType mappedIdEmbeddable = cid.getMappedIdEmbeddableTypeDescriptor();
if ( compositeIdentifierMapping.hasContainingClass() ) { if ( cid.hasContainingClass() ) {
// For id-classes, we might have to transform from the virtual representation to the id-class representation final EmbeddableMappingType virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
// in case the virtual representation contains a to-one attribute, that is mapped by an embeddable in the id-class if ( virtualIdEmbeddable == mappedIdEmbeddable ) {
embedded.getEmbeddableTypeDescriptor().forEachAttributeMapping( return;
(index, attributeMapping) -> { }
final AttributeMapping idClassAttribute = embeddableTypeDescriptor.getAttributeMappings().get( index );
if ( attributeMapping instanceof ToOneAttributeMapping && !( idClassAttribute instanceof ToOneAttributeMapping ) ) { virtualIdEmbeddable.forEachAttributeMapping(
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping; (position, virtualIdAttribute) -> {
final AttributeMapping mappedIdAttribute = mappedIdEmbeddable.getAttributeMapping( position );
if ( virtualIdAttribute instanceof ToOneAttributeMapping
&& !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) virtualIdAttribute;
final ForeignKeyDescriptor fkDescriptor = toOneAttributeMapping.getForeignKeyDescriptor(); final ForeignKeyDescriptor fkDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final Object associationKey = fkDescriptor.getAssociationKeyFromSide( final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
resolvedValues[index], resolvedValues[position],
toOneAttributeMapping.getSideNature().inverse(), toOneAttributeMapping.getSideNature().inverse(),
session session
); );
resolvedValues[index] = associationKey; resolvedValues[position] = associationKey;
} }
} }
); );
} }
} }
else {
embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
}
embeddableTypeDescriptor.setPropertyValues(
compositeInstance,
resolvedValues
);
} }
@Override @Override