Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
assertjVersion = "3.27.6"
hibernateOrmVersion = "7.1.8.Final"
hibernateOrmGradlePluginVersion = "7.1.8.Final"
hibernateOrmVersion = "7.1.9.Final"
hibernateOrmGradlePluginVersion = "7.1.9.Final"
jacksonDatabindVersion = "2.20.1"
jbossLoggingAnnotationVersion = "3.0.4.Final"
jbossLoggingVersion = "3.6.1.Final"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,109 +100,124 @@ public CompletionStage<Void> reactiveResolveInstance(EntityDelayedFetchInitializ
final RowProcessingState rowProcessingState = data.getRowProcessingState();
data.setEntityIdentifier( getIdentifierAssembler().assemble( rowProcessingState ) );

CompletionStage<Void> stage = voidFuture();
if ( data.getEntityIdentifier() == null ) {
data.setInstance( null );
data.setState( State.MISSING );
return voidFuture();
}
else {
final SharedSessionContractImplementor session = rowProcessingState.getSession();

final EntityPersister entityPersister = referencedModelPart.getEntityMappingType().getEntityPersister();
final EntityPersister concreteDescriptor;
if ( getDiscriminatorAssembler() != null ) {
concreteDescriptor = determineConcreteEntityDescriptor( rowProcessingState, getDiscriminatorAssembler(), entityPersister );
if ( concreteDescriptor == null ) {
// If we find no discriminator it means there's no entity in the target table
if ( !referencedModelPart.isOptional() ) {
throw new FetchNotFoundException( entityPersister.getEntityName(), data.getEntityIdentifier() );
}
data.setInstance( null );
data.setState( State.MISSING );
return voidFuture();
}
}
else {
concreteDescriptor = entityPersister;
}

final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( isSelectByUniqueKey() ) {
final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName();
final Type uniqueKeyPropertyType = uniqueKeyPropertyName == null
? concreteDescriptor.getIdentifierType()
: session.getFactory().getRuntimeMetamodels().getReferencedPropertyType( concreteDescriptor.getEntityName(), uniqueKeyPropertyName );
final EntityUniqueKey euk = new EntityUniqueKey(
concreteDescriptor.getEntityName(),
uniqueKeyPropertyName,
data.getEntityIdentifier(),
uniqueKeyPropertyType,
session.getFactory()
);
data.setInstance( persistenceContext.getEntity( euk ) );
if ( data.getInstance() == null ) {
// For unique-key mappings, we always use bytecode-laziness if possible,
// because we can't generate a proxy based on the unique key yet
if ( referencedModelPart.isLazy() ) {
data.setInstance( LazyPropertyInitializer.UNFETCHED_PROPERTY );
}
else {
stage = stage
.thenCompose( v -> ( (ReactiveEntityPersister) concreteDescriptor )
.reactiveLoadByUniqueKey(
uniqueKeyPropertyName,
data.getEntityIdentifier(),
session
) )
.thenAccept( data::setInstance )
.thenAccept( v -> {
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( data.getInstance() != null ) {
persistenceContext.addEntity( euk, data.getInstance() );
}
} );
}
final EntityPersister entityPersister = referencedModelPart.getEntityMappingType().getEntityPersister();
final EntityPersister concreteDescriptor;
if ( getDiscriminatorAssembler() != null ) {
concreteDescriptor = determineConcreteEntityDescriptor(
rowProcessingState,
getDiscriminatorAssembler(),
entityPersister
);
if ( concreteDescriptor == null ) {
// If we find no discriminator it means there's no entity in the target table
if ( !referencedModelPart.isOptional() ) {
throw new FetchNotFoundException( entityPersister.getEntityName(), data.getEntityIdentifier() );
}
stage = stage.thenAccept( v -> {
if ( data.getInstance() != null ) {
data.setInstance( persistenceContext.proxyFor( data.getInstance() ) );
}
} );
data.setInstance( null );
data.setState( State.MISSING );
return voidFuture();
}
else {
final EntityKey entityKey = new EntityKey( data.getEntityIdentifier(), concreteDescriptor );
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
if ( holder != null && holder.getEntity() != null ) {
data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) );
}
// For primary key based mappings we only use bytecode-laziness if the attribute is optional,
// because the non-optionality implies that it is safe to have a proxy
else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) {
}
else {
concreteDescriptor = entityPersister;
}

return initialize( data, null, concreteDescriptor );
}

private CompletionStage<Void> initialize(
ReactiveEntityDelayedFetchInitializerData data,
EntityKey entityKey,
EntityPersister concreteDescriptor) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( isSelectByUniqueKey() ) {
final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName();
final Type uniqueKeyPropertyType = uniqueKeyPropertyName == null
? concreteDescriptor.getIdentifierType()
: session.getFactory()
.getRuntimeMetamodels()
.getReferencedPropertyType( concreteDescriptor.getEntityName(), uniqueKeyPropertyName );
final EntityUniqueKey euk = new EntityUniqueKey(
concreteDescriptor.getEntityName(),
uniqueKeyPropertyName,
data.getEntityIdentifier(),
uniqueKeyPropertyType,
session.getFactory()
);
data.setInstance( persistenceContext.getEntity( euk ) );
CompletionStage<Void> stage = voidFuture();
if ( data.getInstance() == null ) {
// For unique-key mappings, we always use bytecode-laziness if possible,
// because we can't generate a proxy based on the unique key yet
if ( referencedModelPart.isLazy() ) {
data.setInstance( LazyPropertyInitializer.UNFETCHED_PROPERTY );
}
else {
stage = stage.thenCompose( v -> ReactiveQueryExecutorLookup
.extract( session )
.reactiveInternalLoad(
concreteDescriptor.getEntityName(),
data.getEntityIdentifier(),
false,
false
)
stage = stage
.thenCompose( v -> ( (ReactiveEntityPersister) concreteDescriptor )
.reactiveLoadByUniqueKey(
uniqueKeyPropertyName,
data.getEntityIdentifier(),
session
) )
.thenAccept( data::setInstance )
);
.thenAccept( v -> {
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( data.getInstance() != null ) {
persistenceContext.addEntity( euk, data.getInstance() );
}
} );
}
}
return stage.thenAccept( v -> {
if ( data.getInstance() != null ) {
data.setInstance( persistenceContext.proxyFor( data.getInstance() ) );
}
stage = stage
.thenAccept( v -> {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( data.getInstance() );
if ( lazyInitializer != null ) {
lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() );
}
} );
} );
}
else {
CompletionStage<Void> stage = voidFuture();
final EntityKey ek = entityKey == null
? new EntityKey( data.getEntityIdentifier(), concreteDescriptor )
: entityKey;
final EntityHolder holder = persistenceContext.getEntityHolder( ek );
if ( holder != null && holder.getEntity() != null ) {
data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) );
}
// For primary key based mappings we only use bytecode-laziness if the attribute is optional,
// because the non-optionality implies that it is safe to have a proxy
else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) {
data.setInstance( LazyPropertyInitializer.UNFETCHED_PROPERTY );
}
else {
stage = stage.thenCompose( v -> ReactiveQueryExecutorLookup
.extract( session )
.reactiveInternalLoad(
concreteDescriptor.getEntityName(),
data.getEntityIdentifier(),
false,
false
)
.thenAccept( data::setInstance )
);
}
return stage
.thenAccept( v -> {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( data.getInstance() );
if ( lazyInitializer != null ) {
lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() );
}
} );
}
return stage;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture;
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
import static org.hibernate.reactive.util.impl.CompletionStages.supplyStage;
import static org.hibernate.reactive.util.impl.CompletionStages.trueFuture;
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;

Expand Down Expand Up @@ -329,14 +330,26 @@ private void postResolveInstance(ReactiveEntityInitializerData data) {

@Override
public CompletionStage<Void> reactiveInitializeInstance(EntityInitializerData data) {
if ( data.getState() != State.RESOLVED ) {
return voidFuture();
}
if ( !skipInitialization( data ) ) {
assert consistentInstance( data );
return reactiveInitializeEntityInstance( (ReactiveEntityInitializerData) data );
if ( data.getState() == State.RESOLVED ) {
if ( !skipInitialization( data ) ) {
assert consistentInstance( data );
return reactiveInitializeEntityInstance( (ReactiveEntityInitializerData) data );
}
else {
if ( data.getRowProcessingState().needsResolveState() ) {
// A sub-initializer might have taken responsibility for this entity,
// but we still need to resolve the state to correctly populate a query cache
resolveState( data );
}
final ReactiveEntityInitializerData reactiveData = (ReactiveEntityInitializerData) data;
if ( getEntityDescriptor().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
&& reactiveData.getEntityHolder().getEntityInitializer() != this
&& reactiveData.getEntityHolder().isInitialized() ) {
updateInitializedEntityInstance( data );
}
}
data.setState( State.INITIALIZED );
}
data.setState( State.INITIALIZED );
return voidFuture();
}

Expand All @@ -348,7 +361,7 @@ protected CompletionStage<Void> reactiveInitializeEntityInstance(ReactiveEntityI

return reactiveExtractConcreteTypeStateValues( data )
.thenAccept( resolvedEntityState -> {

rowProcessingState.getJdbcValuesSourceProcessingState().registerLoadingEntityHolder( data.getEntityHolder() );
preLoad( data, resolvedEntityState );

if ( isPersistentAttributeInterceptable( data.getEntityInstanceForNotify() ) ) {
Expand All @@ -372,18 +385,14 @@ protected CompletionStage<Void> reactiveInitializeEntityInstance(ReactiveEntityI
final Object version = getVersionAssembler() != null ? getVersionAssembler().assemble( rowProcessingState ) : null;
final Object rowId = getRowIdAssembler() != null ? getRowIdAssembler().assemble( rowProcessingState ) : null;

// from the perspective of Hibernate, an entity is read locked as soon as it is read
// so regardless of the requested lock mode, we upgrade to at least the read level
final LockMode lockModeToAcquire = data.getLockMode() == LockMode.NONE ? LockMode.READ : data.getLockMode();

final EntityEntry entityEntry = persistenceContext.addEntry(
data.getEntityInstanceForNotify(),
Status.LOADING,
resolvedEntityState,
rowId,
data.getEntityKey().getIdentifier(),
version,
lockModeToAcquire,
lockModeToAcquire( data ),
true,
data.getConcreteDescriptor(),
false
Expand All @@ -404,16 +413,15 @@ protected CompletionStage<Void> reactiveInitializeEntityInstance(ReactiveEntityI
statistics.loadEntity( data.getConcreteDescriptor().getEntityName() );
}
}
updateCaches(
data,
session,
session.getPersistenceContextInternal(),
resolvedEntityState,
version
);
updateCaches( data, session, session.getPersistenceContextInternal(), resolvedEntityState, version );
} );
}

// Hibernate ORM has a similar method, but it checks if we are in a transaction first
private static LockMode lockModeToAcquire(ReactiveEntityInitializerData data) {
return data.getLockMode() == LockMode.NONE ? LockMode.READ : data.getLockMode();
}

protected CompletionStage<Object[]> reactiveExtractConcreteTypeStateValues(ReactiveEntityInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final Object[] values = new Object[data.getConcreteDescriptor().getNumberOfAttributeMappings()];
Expand Down Expand Up @@ -447,7 +455,9 @@ protected CompletionStage<Void> reactiveResolveEntityInstance1(ReactiveEntityIni
else {
data.setInstance( proxy );
if ( Hibernate.isInitialized( data.getInstance() ) ) {
data.setState( State.INITIALIZED );
if ( data.getEntityHolder().isInitialized() ) {
data.setState( State.INITIALIZED );
}
data.setEntityInstanceForNotify( Hibernate.unproxy( data.getInstance() ) );
}
else {
Expand Down Expand Up @@ -755,7 +765,6 @@ private CompletionStage<Boolean> initializeId(ReactiveEntityInitializerData data
return trueFuture();
}
else {
//noinspection unchecked
final Initializer<InitializerData> initializer = (Initializer<InitializerData>) getIdentifierAssembler().getInitializer();
if ( initializer != null ) {
final InitializerData subData = initializer.getData( rowProcessingState );
Expand Down Expand Up @@ -807,8 +816,7 @@ public CompletionStage<Void> forEachReactiveSubInitializer(
BiFunction<ReactiveInitializer<?>, RowProcessingState, CompletionStage<Void>> consumer,
InitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
return voidFuture()
.thenCompose( v -> {
return supplyStage( () -> {
if ( getKeyAssembler() != null ) {
final Initializer<?> initializer = getKeyAssembler().getInitializer();
if ( initializer != null ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,15 @@ protected CompletionStage<Void> reactiveInitialize(EntitySelectFetchInitializerD
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final EntityKey entityKey = new EntityKey( data.getEntityIdentifier(), concreteDescriptor );

final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
return initialiaze( data, persistenceContext.getEntityHolder( entityKey ), (ReactiveQueryProducer) session, persistenceContext );
}

private CompletionStage<Void> initialiaze(
ReactiveEntitySelectFetchInitializerData data,
EntityHolder holder,
ReactiveQueryProducer session,
PersistenceContext persistenceContext) {
if ( holder != null ) {
data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) );
if ( holder.getEntityInitializer() == null ) {
Expand All @@ -154,7 +160,7 @@ else if ( data.getInstance() == null ) {
data.setState( State.INITIALIZED );
final String entityName = concreteDescriptor.getEntityName();

return ( (ReactiveQueryProducer) session ).reactiveInternalLoad(
return session.reactiveInternalLoad(
entityName,
data.getEntityIdentifier(),
true,
Expand All @@ -176,10 +182,10 @@ else if ( data.getInstance() == null ) {
throw new FetchNotFoundException( entityName, data.getEntityIdentifier() );
}
}
rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
persistenceContext.claimEntityHolderIfPossible(
new EntityKey( data.getEntityIdentifier(), concreteDescriptor ),
null,
rowProcessingState.getJdbcValuesSourceProcessingState(),
data.getRowProcessingState().getJdbcValuesSourceProcessingState(),
this
);
}
Expand Down
Loading
Loading