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:
parent
142299e7a8
commit
42d1fcca19
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue