-
Notifications
You must be signed in to change notification settings - Fork 96
Reactive find with lock in Quarkus with reactive hibernate #2117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,18 +30,24 @@ | |
import org.hibernate.engine.spi.SessionImplementor; | ||
import org.hibernate.engine.spi.SharedSessionContractImplementor; | ||
import org.hibernate.engine.spi.Status; | ||
import org.hibernate.event.service.spi.EventListenerGroup; | ||
import org.hibernate.event.spi.PostLoadEvent; | ||
import org.hibernate.event.spi.PostLoadEventListener; | ||
import org.hibernate.persister.collection.CollectionPersister; | ||
import org.hibernate.persister.entity.EntityPersister; | ||
import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; | ||
import org.hibernate.reactive.logging.impl.Log; | ||
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; | ||
import org.hibernate.reactive.session.ReactiveSession; | ||
import org.hibernate.sql.exec.spi.Callback; | ||
import org.hibernate.sql.results.graph.entity.EntityInitializer; | ||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; | ||
import org.hibernate.sql.results.spi.LoadContexts; | ||
|
||
import static java.lang.invoke.MethodHandles.lookup; | ||
import static org.hibernate.reactive.logging.impl.LoggerFactory.make; | ||
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; | ||
import static org.hibernate.reactive.util.impl.CompletionStages.loop; | ||
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; | ||
|
||
/** | ||
|
@@ -456,7 +462,7 @@ | |
|
||
@Override | ||
public void postLoad(JdbcValuesSourceProcessingState processingState, Consumer<EntityHolder> loadedConsumer) { | ||
delegate.postLoad( processingState, loadedConsumer ); | ||
throw LOG.nonReactiveMethodCall( "reactivePostLoad(JdbcValuesSourceProcessingState, Consumer<EntityHolder>) )" ); | ||
} | ||
|
||
@Internal | ||
|
@@ -710,4 +716,76 @@ | |
public NaturalIdResolutions getNaturalIdResolutions() { | ||
return delegate.getNaturalIdResolutions(); | ||
} | ||
|
||
/** | ||
* Reactive version of {@link StatefulPersistenceContext#postLoad(JdbcValuesSourceProcessingState, Consumer)} | ||
* | ||
*/ | ||
public CompletionStage<Void> reactivePostLoad(JdbcValuesSourceProcessingState processingState, Consumer<EntityHolder> holderConsumer) { | ||
final ReactiveCallbackImpl callback = (ReactiveCallbackImpl) processingState.getExecutionContext().getCallback(); | ||
|
||
if ( processingState.getLoadingEntityHolders() != null ) { | ||
final EventListenerGroup<PostLoadEventListener> listenerGroup = | ||
getSession().getFactory().getEventListenerGroups().eventListenerGroup_POST_LOAD; | ||
final PostLoadEvent postLoadEvent = processingState.getPostLoadEvent(); | ||
return loop( | ||
processingState.getLoadingEntityHolders(), entityHolder -> | ||
processLoadedEntityHolder( | ||
entityHolder, | ||
listenerGroup, | ||
postLoadEvent, | ||
callback, | ||
holderConsumer | ||
)) | ||
.thenAccept( v -> processingState.getLoadingEntityHolders().clear() ); | ||
} | ||
if ( processingState.getReloadedEntityHolders() != null ) { | ||
return loop( | ||
processingState.getLoadingEntityHolders(), entityHolder -> | ||
processLoadedEntityHolder( | ||
entityHolder, | ||
null, | ||
null, | ||
callback, | ||
holderConsumer | ||
)) | ||
.thenAccept( v -> processingState.getLoadingEntityHolders().clear() ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think here it should be using |
||
} | ||
return voidFuture(); | ||
} | ||
|
||
/** | ||
* Reactive version of {@link StatefulPersistenceContext#processLoadedEntityHolder(EntityHolder, EventListenerGroup, PostLoadEvent, Callback, Consumer)} | ||
*/ | ||
private CompletionStage<Void> processLoadedEntityHolder( | ||
EntityHolder holder, | ||
EventListenerGroup<PostLoadEventListener> listenerGroup, | ||
PostLoadEvent postLoadEvent, | ||
ReactiveCallbackImpl callback, | ||
Consumer<EntityHolder> holderConsumer) { | ||
if ( holderConsumer != null ) { | ||
holderConsumer.accept( holder ); | ||
} | ||
if ( holder.getEntity() == null ) { | ||
// It's possible that we tried to load an entity and found out it doesn't exist, | ||
// in which case we added an entry with a null proxy and entity. | ||
// Remove that empty entry on post load to avoid unwanted side effects | ||
getEntitiesByKey().remove( holder.getEntityKey() ); | ||
} | ||
else { | ||
if ( postLoadEvent != null ) { | ||
postLoadEvent.reset(); | ||
postLoadEvent.setEntity( holder.getEntity() ) | ||
.setId( holder.getEntityKey().getIdentifier() ) | ||
.setPersister( holder.getDescriptor() ); | ||
listenerGroup.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad ); | ||
|
||
if ( callback != null ) { | ||
return callback | ||
.invokeReactiveLoadActions( holder.getEntity(), holder.getDescriptor(), getSession() ) | ||
.thenAccept( v -> holder.resetEntityInitialier() ); | ||
} | ||
} | ||
} | ||
return voidFuture(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright: Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.reactive.engine.impl; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.concurrent.CompletionStage; | ||
|
||
import org.hibernate.engine.spi.SharedSessionContractImplementor; | ||
import org.hibernate.loader.ast.spi.AfterLoadAction; | ||
import org.hibernate.metamodel.mapping.EntityMappingType; | ||
import org.hibernate.reactive.loader.ast.spi.ReactiveAfterLoadAction; | ||
import org.hibernate.reactive.logging.impl.Log; | ||
import org.hibernate.sql.exec.spi.Callback; | ||
|
||
import static java.lang.invoke.MethodHandles.lookup; | ||
import static org.hibernate.reactive.logging.impl.LoggerFactory.make; | ||
import static org.hibernate.reactive.util.impl.CompletionStages.loop; | ||
|
||
/** | ||
* Reactive equivalent of {@link org.hibernate.sql.exec.internal.CallbackImpl} | ||
*/ | ||
public class ReactiveCallbackImpl implements Callback { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can remove some code here if
But, we can do it in a separate PR, otherwise we will have to wait for the next ORM release. |
||
private static final Log LOG = make( Log.class, lookup() ); | ||
|
||
private final List<ReactiveAfterLoadAction> afterLoadActions; | ||
|
||
public ReactiveCallbackImpl() { | ||
this.afterLoadActions = new ArrayList<>( 1 ); | ||
} | ||
|
||
@Override | ||
public void registerAfterLoadAction(AfterLoadAction afterLoadAction) { | ||
throw LOG.nonReactiveMethodCall( "registerReactiveAfterLoadAction(ReactiveCallbackImpl)" ); | ||
} | ||
|
||
public void registerReactiveAfterLoadAction(ReactiveAfterLoadAction afterLoadAction) { | ||
afterLoadActions.add( afterLoadAction ); | ||
} | ||
|
||
@Override | ||
public void invokeAfterLoadActions( | ||
Object entity, | ||
EntityMappingType entityMappingType, | ||
SharedSessionContractImplementor session) { | ||
throw LOG.nonReactiveMethodCall( "invokeAfterLoadActions(Object, EntityMappingType, SharedSessionContractImplementor)" ); | ||
} | ||
|
||
/** | ||
* Reactive version of {@link org.hibernate.sql.exec.internal.CallbackImpl#invokeAfterLoadActions(Object, EntityMappingType, SharedSessionContractImplementor)} | ||
*/ | ||
public CompletionStage<Void> invokeReactiveLoadActions( | ||
Object entity, | ||
EntityMappingType entityMappingType, | ||
SharedSessionContractImplementor session) { | ||
return loop( | ||
afterLoadActions, afterLoadAction -> | ||
afterLoadAction.reactiveAfterLoad( entity, entityMappingType, session ) | ||
); | ||
} | ||
|
||
@Override | ||
public boolean hasAfterLoadActions() { | ||
return !afterLoadActions.isEmpty(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright: Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.reactive.loader.ast.spi; | ||
|
||
import java.util.concurrent.CompletionStage; | ||
|
||
import org.hibernate.engine.spi.SharedSessionContractImplementor; | ||
import org.hibernate.metamodel.mapping.EntityMappingType; | ||
|
||
/** | ||
* Reactive version of {@link org.hibernate.loader.ast.spi.AfterLoadAction} | ||
*/ | ||
public interface ReactiveAfterLoadAction { | ||
/** | ||
* @see org.hibernate.loader.ast.spi.AfterLoadAction#afterLoad(Object, EntityMappingType, SharedSessionContractImplementor) | ||
* | ||
* The action trigger - the {@code entity} is being loaded | ||
*/ | ||
CompletionStage<Void> reactiveAfterLoad( | ||
Object entity, | ||
EntityMappingType entityMappingType, | ||
SharedSessionContractImplementor session); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Hibernate ORM the code looks like this:
There is not a
return
at the end of the first if-then block. It means that the two conditionsgetLoadingEntityHolders() != null
andprocessingState.getReloadedEntityHolders() != null
could both be true and both blocks should be evaluated.Did you add a
return loop( ...
because you understand what's going on or is this an error?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably it can be avoided
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?