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
29ed0a0566
commit
142299e7a8
|
@ -43,7 +43,12 @@ 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 static final Object NULL_MARKER = new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Composite NULL_MARKER";
|
||||
}
|
||||
};
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
private final EmbeddableValuedModelPart embedded;
|
||||
|
@ -143,10 +148,55 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState processingState) {
|
||||
reallyResolve( processingState );
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
private void reallyResolve(RowProcessingState processingState) {
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState processingState) {
|
||||
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||
"Initializing composite instance [%s]",
|
||||
navigablePath
|
||||
);
|
||||
|
||||
extractRowState( processingState );
|
||||
prepareCompositeInstance( processingState );
|
||||
handleParentInjection( processingState );
|
||||
|
||||
if ( !createEmptyCompositesEnabled && allValuesNull == TRUE ) {
|
||||
compositeInstance = NULL_MARKER;
|
||||
}
|
||||
else {
|
||||
notifyResolutionListeners( compositeInstance );
|
||||
if ( compositeInstance instanceof HibernateProxy ) {
|
||||
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
|
||||
if ( parentInitializer != this ) {
|
||||
( (FetchParentAccess) parentInitializer ).registerResolutionListener(
|
||||
entity -> setPropertyValuesOnTarget( entity, processingState.getSession() )
|
||||
);
|
||||
}
|
||||
else {
|
||||
Object target = representationStrategy
|
||||
.getInstantiator()
|
||||
.instantiate( null, sessionFactory );
|
||||
setPropertyValuesOnTarget( target, processingState.getSession() );
|
||||
( (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 ) {
|
||||
setPropertyValuesOnTarget( compositeInstance, processingState.getSession() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareCompositeInstance(RowProcessingState processingState) {
|
||||
if ( compositeInstance != null ) {
|
||||
return;
|
||||
}
|
||||
|
@ -188,51 +238,6 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState processingState) {
|
||||
EmbeddableLoadingLogger.INSTANCE.debugf(
|
||||
"Initializing composite instance [%s]",
|
||||
navigablePath
|
||||
);
|
||||
|
||||
handleParentInjection( processingState );
|
||||
|
||||
extractRowState( processingState );
|
||||
|
||||
if ( !createEmptyCompositesEnabled && allValuesNull == TRUE ) {
|
||||
compositeInstance = null;
|
||||
}
|
||||
else {
|
||||
notifyResolutionListeners( compositeInstance );
|
||||
if ( compositeInstance instanceof HibernateProxy ) {
|
||||
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
|
||||
if ( parentInitializer != this ) {
|
||||
( (FetchParentAccess) parentInitializer ).registerResolutionListener(
|
||||
entity -> setPropertyValuesOnTarget( entity, processingState.getSession() )
|
||||
);
|
||||
}
|
||||
else {
|
||||
Object target = representationStrategy
|
||||
.getInstantiator()
|
||||
.instantiate( null, sessionFactory );
|
||||
setPropertyValuesOnTarget( target, processingState.getSession() );
|
||||
( (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 ) {
|
||||
setPropertyValuesOnTarget( compositeInstance, processingState.getSession() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void extractRowState(RowProcessingState processingState) {
|
||||
allValuesNull = true;
|
||||
for ( int i = 0; i < assemblers.size(); i++ ) {
|
||||
|
|
|
@ -27,39 +27,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
public class NestedIdClassTests {
|
||||
@Test
|
||||
public void smokeTest(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final String qry = "from Payment p join fetch p.id.order o join fetch o.id.customer";
|
||||
session.createQuery( qry ).list();
|
||||
});
|
||||
scope.inTransaction( (session) -> {
|
||||
final String qry = "from Payment p join fetch p.order o join fetch o.customer";
|
||||
session.createQuery( qry ).list();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smokeTest2(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final String qry = "from Payment p";
|
||||
final Payment payment = session.createQuery( qry, Payment.class ).uniqueResult();
|
||||
assertThat( payment ).isNotNull();
|
||||
assertThat( payment.accountNumber ).isNotNull();
|
||||
assertThat( payment.order ).isNotNull();
|
||||
|
||||
assertThat( payment.order.orderNumber ).isNotNull();
|
||||
assertThat( payment.order.customer ).isNotNull();
|
||||
|
||||
assertThat( payment.order.customer.id ).isNotNull();
|
||||
assertThat( payment.order.customer.name ).isNotNull();
|
||||
});
|
||||
scope.inTransaction( (session) -> {
|
||||
final String qry = "from Payment p join fetch p.order o join fetch o.customer";
|
||||
session.createQuery( qry ).list();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smokeTest3(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Payment payment = session.get( Payment.class, new PaymentId( new OrderId( 1, 1 ), "123" ) );
|
||||
assertThat( payment ).isNotNull();
|
||||
|
|
Loading…
Reference in New Issue