diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java index 3dfc8dcd0..6d87416b4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java @@ -37,9 +37,9 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; 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; @@ -79,8 +79,9 @@ private class NonLazyCollectionInitializer implements Consumer nonLazyCollection) { if ( !nonLazyCollection.wasInitialized() ) { - stage = stage.thenCompose( v -> ( (ReactiveSession) getSession() ) - .reactiveInitializeCollection( nonLazyCollection, false ) ); + stage = stage.thenCompose( v -> + ( (ReactiveSharedSessionContractImplementor) getSession() ) + .reactiveInitializeCollection( nonLazyCollection, false ) ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java index 4bc488bb6..bb72946ad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java @@ -110,20 +110,18 @@ else if ( target instanceof Map map ) { } else { final PersistenceContext persistenceContext = session.getPersistenceContext(); - final PersistentCollection collectionHolder = persistenceContext.getCollectionHolder( target ); - if ( collectionHolder != null ) { - if ( collectionHolder instanceof PersistentArrayHolder arrayHolder ) { - persistenceContext.removeCollectionHolder( target ); - arrayHolder.beginRead(); - final PluralAttributeMapping attributeMapping = - persistenceContext.getCollectionEntry( collectionHolder ) - .getLoadedPersister().getAttributeMapping(); - arrayHolder.injectLoadedState( attributeMapping, null ); - arrayHolder.endRead(); - arrayHolder.dirty(); - persistenceContext.addCollectionHolder( collectionHolder ); - return arrayHolder.getArray(); - } + if ( persistenceContext.getCollectionHolder( target ) + instanceof PersistentArrayHolder arrayHolder ) { + persistenceContext.removeCollectionHolder( target ); + arrayHolder.beginRead(); + final PluralAttributeMapping attributeMapping = + persistenceContext.getCollectionEntry( arrayHolder ) + .getLoadedPersister().getAttributeMapping(); + arrayHolder.injectLoadedState( attributeMapping, null ); + arrayHolder.endRead(); + arrayHolder.dirty(); + persistenceContext.addCollectionHolder( arrayHolder ); + return arrayHolder.getArray(); } } return null; @@ -142,8 +140,9 @@ private static Object replaceUninitializedOriginal( // A managed entity with an uninitialized collection is being merged, // We need to replace any detached entities in the queued operations // with managed copies. - final AbstractPersistentCollection pc = (AbstractPersistentCollection) original; - pc.replaceQueuedOperationValues( + final AbstractPersistentCollection collection = + (AbstractPersistentCollection) original; + collection.replaceQueuedOperationValues( session.getFactory() .getMappingMetamodel() .getCollectionDescriptor( type.getRole() ), copyCache @@ -182,18 +181,20 @@ private static CompletionStage replaceOriginal( ).thenCompose( result -> { if ( original == target ) { // get the elements back into the target making sure to handle dirty flag - final boolean wasClean = - target instanceof PersistentCollection collection - && !collection.isDirty(); //TODO: this is a little inefficient, don't need to do a whole // deep replaceElements() call - return replaceElements( type, result, target, owner, copyCache, session ) - .thenApply( unused -> { - if ( wasClean ) { - ( (PersistentCollection) target ).clearDirty(); - } - return target; - } ); + final CompletionStage replaced = + replaceElements( type, result, target, owner, copyCache, session ); + if ( target instanceof PersistentCollection collection + && !collection.isDirty() ) { + return replaced.thenApply( unused -> { + collection.clearDirty(); + return target; + } ); + } + else { + return replaced.thenApply( unused -> target ); + } } else { return completedFuture( result ); @@ -220,7 +221,7 @@ else if ( type instanceof CustomCollectionType ) { else if ( type instanceof MapType ) { return replaceMapTypeElements( type, - (Map) original, + (Map) original, (Map) target, owner, copyCache, @@ -247,53 +248,59 @@ private static CompletionStage replaceCollectionTypeElements( Map copyCache, SessionImplementor session) { result.clear(); - // copy elements into newly empty target collection final Type elemType = type.getElementType( session.getFactory() ); - return loop( - (Collection) original, o -> getReplace( elemType, o, owner, session, copyCache ) - .thenAccept( result::add ) - ).thenCompose( unused -> { - // if the original is a PersistentCollection, and that original - // was not flagged as dirty, then reset the target's dirty flag - // here after the copy operation. - //

- // One thing to be careful of here is a "bare" original collection - // in which case we should never ever ever reset the dirty flag - // on the target because we simply do not know... - if ( original instanceof PersistentCollection originalPersistentCollection - && result instanceof PersistentCollection resultPersistentCollection ) { - return preserveSnapshot( - originalPersistentCollection, resultPersistentCollection, - elemType, owner, copyCache, session - ).thenApply( v -> { - if ( !originalPersistentCollection.isDirty() ) { - resultPersistentCollection.clearDirty(); - } - return result; - } ); - } - else { - return completedFuture( result ); - } - } ); + return loop( (Collection) original, + o -> getReplace( elemType, o, owner, session, copyCache ) + .thenAccept( result::add ) ) + .thenCompose( v -> preserveSnapshotIfNecessary( original, result, owner, copyCache, session, elemType ) ); + } + + private static CompletionStage preserveSnapshotIfNecessary( + Object original, + Collection result, + Object owner, + Map copyCache, + SessionImplementor session, + Type elemType) { + // if the original is a PersistentCollection, and that original + // was not flagged as dirty, then reset the target's dirty flag + // here after the copy operation. + //

+ // One thing to be careful of here is a "bare" original collection + // in which case we should never ever ever reset the dirty flag + // on the target because we simply do not know... + if ( original instanceof PersistentCollection originalCollection + && result instanceof PersistentCollection resultCollection ) { + return preserveSnapshot( + originalCollection, resultCollection, + elemType, owner, copyCache, session + ).thenApply( v -> { + if ( !originalCollection.isDirty() ) { + resultCollection.clearDirty(); + } + return result; + } ); + } + else { + return completedFuture( result ); + } } private static CompletionStage replaceMapTypeElements( CollectionType type, - Map original, + Map original, Map target, Object owner, Map copyCache, SessionImplementor session) { - final CollectionPersister persister = session.getFactory().getRuntimeMetamodels() - .getMappingMetamodel().getCollectionDescriptor( type.getRole() ); - final Map result = target; - result.clear(); - + final CollectionPersister persister = + session.getFactory().getRuntimeMetamodels().getMappingMetamodel() + .getCollectionDescriptor( type.getRole() ); + target.clear(); return loop( original.entrySet(), entry -> { - final Map.Entry me = entry; + final Map.Entry me = entry; return getReplace( persister.getIndexType(), me.getKey(), owner, session, copyCache ) .thenCompose( key -> getReplace( persister.getElementType(), @@ -301,10 +308,10 @@ private static CompletionStage replaceMapTypeElements( owner, session, copyCache - ).thenAccept( value -> result.put( key, value ) ) + ).thenAccept( value -> target.put( key, value ) ) ); } - ).thenApply( unused -> result ); + ).thenApply( unused -> target); } private static CompletionStage replaceArrayTypeElements( @@ -325,8 +332,8 @@ private static CompletionStage replaceArrayTypeElements( } final Type elemType = type.getElementType( session.getFactory() ); - return loop( - 0, length, i -> getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) + return loop( 0, length, + i -> getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) .thenApply( o -> { Array.set( result, i, o ); return result; @@ -345,18 +352,14 @@ private static CompletionStage getReplace( private static CompletionStage getReplace( Type elemType, - Object o, + Object object, Object target, Object owner, SessionImplementor session, Map copyCache) { - if ( elemType instanceof EntityType ) { - return EntityTypes.replace( (EntityType) elemType, o, target, session, owner, copyCache ); - } - else { - final Object replace = elemType.replace( o, target, session, owner, copyCache ); - return completedFuture( replace ); - } + return elemType instanceof EntityType entityType + ? EntityTypes.replace( entityType, object, target, session, owner, copyCache ) + : completedFuture( elemType.replace( object, target, session, owner, copyCache) ); } /** @@ -374,7 +377,9 @@ private static CompletionStage preserveSnapshot( return createSnapshot( original, result, elemType, owner, copyCache, session ) .thenAccept( serializable -> ce.resetStoredSnapshot( result, serializable ) ); } - return voidFuture(); + else { + return voidFuture(); + } } /** @@ -412,8 +417,8 @@ private static CompletionStage createArraySnapshot( Object owner, Map copyCache, SessionImplementor session) { - return loop( - 0, array.length, i -> getReplace( elemType, array[i], owner, session, copyCache ) + return loop( 0, array.length, + i -> getReplace( elemType, array[i], owner, session, copyCache ) .thenAccept( o -> array[i] = o ) ).thenApply( unused -> array ); } @@ -421,29 +426,26 @@ private static CompletionStage createArraySnapshot( /** * @see CollectionType#createMapSnapshot(Map, PersistentCollection, Type, Object, Map, SharedSessionContractImplementor) */ - private static CompletionStage createMapSnapshot( - Map map, + private static CompletionStage createMapSnapshot( + Map map, PersistentCollection result, Type elemType, Object owner, Map copyCache, SessionImplementor session) { final Map resultSnapshot = (Map) result.getStoredSnapshot(); - final Map targetMap; - if ( map instanceof SortedMap sortedMap ) { - //noinspection unchecked, rawtypes - targetMap = new TreeMap( sortedMap.comparator() ); - } - else { - targetMap = mapOfSize( map.size() ); - } - return loop( - map.entrySet(), entry -> - getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) - .thenAccept( newValue -> { - final Object key = entry.getKey(); - targetMap.put( key == entry.getValue() ? newValue : key, newValue ); - } ) + final Map targetMap = + map instanceof SortedMap sortedMap + ? new TreeMap<>( sortedMap.comparator() ) + : mapOfSize(map.size()); + return loop( map.entrySet(), + entry -> getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) + .thenAccept( newValue -> { + final K key = entry.getKey(); + final V value = entry.getValue(); + //noinspection unchecked + targetMap.put( key == value ? (K) newValue : key, (V) newValue ); + } ) ).thenApply( v -> (Serializable) targetMap ); } @@ -457,8 +459,8 @@ private static CompletionStage createListSnapshot( Map copyCache, SessionImplementor session) { final ArrayList targetList = new ArrayList<>( list.size() ); - return loop( - list, obj -> getReplace( elemType, obj, owner, session, copyCache ) + return loop( list, + obj -> getReplace( elemType, obj, owner, session, copyCache ) .thenAccept( targetList::add ) ).thenApply( unused -> targetList ); } @@ -472,9 +474,9 @@ private static Object instantiateResultIfNecessary(CollectionType type, Object o // by default just use an unanticipated capacity since we don't // know how to extract the capacity to use from original here... return target == null - || target == original - || target == UNFETCHED_PROPERTY - || target instanceof PersistentCollection collection && collection.isWrapper( original ) + || target == original + || target == UNFETCHED_PROPERTY + || target instanceof PersistentCollection collection && collection.isWrapper( original ) ? type.instantiate( -1 ) : target; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index 0bb0c75ae..9176e853c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -22,9 +22,10 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; -import org.hibernate.reactive.session.impl.ReactiveSessionImpl; import org.hibernate.type.CollectionType; import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; @@ -158,8 +159,8 @@ public static CompletionStage replace( final Object owner, final Map copyCache) { Object[] copied = new Object[original.length]; - return loop( - 0, types.length, i -> replace( original, target, types, session, owner, copyCache, i, copied ) + return loop( 0, types.length, + i -> replace( original, target, types, session, owner, copyCache, i, copied ) ).thenApply( v -> copied ); } @@ -175,8 +176,7 @@ public static CompletionStage replace( final Map copyCache, final ForeignKeyDirection foreignKeyDirection) { Object[] copied = new Object[original.length]; - return loop( - 0, types.length, + return loop( 0, types.length, i -> replace( original, target, types, session, owner, copyCache, foreignKeyDirection, i, copied ) ).thenApply( v -> copied ); } @@ -268,7 +268,7 @@ private static CompletionStage resolveIdOrUniqueKey( // as a ComponentType. In the case that the entity is unfetched, we need to // explicitly fetch it here before calling replace(). (Note that in Hibernate // ORM this is unnecessary due to transparent lazy fetching.) - return ( (ReactiveSessionImpl) session ) + return ( (ReactiveQueryProducer) session ) .reactiveFetch( id, true ) .thenCompose( fetched -> { Object idOrUniqueKey = entityType @@ -340,10 +340,11 @@ private static CompletionStage getIdentifierFromHibernateProxy( EntityType entityType, HibernateProxy proxy, SharedSessionContractImplementor session) { - LazyInitializer initializer = proxy.getHibernateLazyInitializer(); + final LazyInitializer initializer = proxy.getHibernateLazyInitializer(); final String entityName = initializer.getEntityName(); final Object identifier = initializer.getIdentifier(); - return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier ) + return ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveImmediateLoad( entityName, identifier ) .thenApply( entity -> { checkEntityFound( session, entityName, identifier, entity ); initializer.setSession( session ); @@ -372,7 +373,8 @@ private static CompletionStage loadHibernateProxyEntity( LazyInitializer initializer = ( (HibernateProxy) entity ).getHibernateLazyInitializer(); final String entityName = initializer.getEntityName(); final Object identifier = initializer.getIdentifier(); - return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier ) + return ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveImmediateLoad( entityName, identifier ) .thenApply( result -> { checkEntityFound( session, entityName, identifier, result ); return result; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java index 1ab340380..9bdc5742a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java @@ -9,6 +9,7 @@ import java.util.concurrent.CompletionStage; +import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.PersistenceContext; import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture; @@ -22,5 +23,9 @@ default CompletionStage reactiveAutoFlushIfRequired(Set querySp return falseFuture(); } + CompletionStage reactiveImmediateLoad(String entityName, Object id); + + CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing); + PersistenceContext getPersistenceContext(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java index 20466abd2..5547fcc74 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java @@ -23,9 +23,9 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; -import org.hibernate.sql.results.internal.ResultsHelper; import org.hibernate.stat.spi.StatisticsImplementor; +import static org.hibernate.event.internal.DefaultInitializeCollectionEventListener.handlePotentiallyEmptyCollection; import static org.hibernate.pretty.MessageHelper.collectionInfoString; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; @@ -59,7 +59,8 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection final CollectionPersister loadedPersister = ce.getLoadedPersister(); final Object loadedKey = ce.getLoadedKey(); if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Initializing collection {0}", collectionInfoString( loadedPersister, collection, loadedKey, source ) ); + LOG.tracev( "Initializing collection {0}", + collectionInfoString( loadedPersister, collection, loadedKey, source ) ); LOG.trace( "Checking second-level cache" ); } @@ -76,11 +77,8 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection } return ( (ReactiveCollectionPersister) loadedPersister ) .reactiveInitialize( loadedKey, source ) - .thenApply( list -> { - handlePotentiallyEmptyCollection( collection, source, ce, loadedPersister ); - return list; - } ) - .thenAccept( list -> { + .thenAccept( v -> { + handlePotentiallyEmptyCollection( collection, source.getPersistenceContext(), ce, loadedPersister ); if ( LOG.isTraceEnabled() ) { LOG.trace( "Collection initialized" ); } @@ -93,23 +91,6 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection } } - private void handlePotentiallyEmptyCollection( - PersistentCollection collection, - SessionImplementor source, - CollectionEntry ce, - CollectionPersister loadedPersister) { - if ( !collection.wasInitialized() ) { - collection.initializeEmptyCollection( loadedPersister ); - ResultsHelper.finalizeCollectionLoading( - source.getPersistenceContext(), - loadedPersister, - collection, - ce.getLoadedKey(), - true - ); - } - } - /** * Try to initialize a collection from the cache * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c3608061b..aab884e76 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1745,18 +1745,6 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp @Incubating Uni upsert(Object entity); - /** - * Use a SQL {@code merge into} statement to perform an upsert. - * - * @param entityName The entityName for the entity to be merged - * @param entity a detached entity instance - * @throws org.hibernate.TransientObjectException is the entity is transient - * - * @see org.hibernate.StatelessSession#upsert(String, Object) - */ - @Incubating - Uni upsert(String entityName, Object entity); - /** * Use a SQL {@code merge into} statement to perform * an upsert on multiple rows using the size of the given array diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 8a4149104..c80be96ed 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -332,36 +332,22 @@ public Uni lock(Object entity, LockOptions lockOptions) { @Override public FlushMode getFlushMode() { - switch ( delegate.getHibernateFlushMode() ) { - case MANUAL: - return FlushMode.MANUAL; - case COMMIT: - return FlushMode.COMMIT; - case AUTO: - return FlushMode.AUTO; - case ALWAYS: - return FlushMode.ALWAYS; - default: - throw LOG.impossibleFlushModeIllegalState(); - } + return switch (delegate.getHibernateFlushMode()) { + case MANUAL -> FlushMode.MANUAL; + case COMMIT -> FlushMode.COMMIT; + case AUTO -> FlushMode.AUTO; + case ALWAYS -> FlushMode.ALWAYS; + }; } @Override public Mutiny.Session setFlushMode(FlushMode flushMode) { - switch ( flushMode ) { - case COMMIT: - delegate.setHibernateFlushMode( FlushMode.COMMIT ); - break; - case AUTO: - delegate.setHibernateFlushMode( FlushMode.AUTO ); - break; - case MANUAL: - delegate.setHibernateFlushMode( FlushMode.MANUAL ); - break; - case ALWAYS: - delegate.setHibernateFlushMode( FlushMode.ALWAYS ); - break; - } + delegate.setHibernateFlushMode( switch ( flushMode ) { + case COMMIT -> org.hibernate.FlushMode.COMMIT; + case AUTO -> org.hibernate.FlushMode.AUTO; + case MANUAL -> org.hibernate.FlushMode.MANUAL; + case ALWAYS -> org.hibernate.FlushMode.ALWAYS; + } ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index f5a6352c1..21602c48f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -240,11 +240,6 @@ public Uni upsert(Object entity) { return uni( () -> delegate.reactiveUpsert( entity ) ); } - @Override - public Uni upsert(String entityName, Object entity) { - return uni( () -> delegate.reactiveUpsert( entityName, entity ) ); - } - @Override public Uni upsertAll(Object... entities) { return uni( () -> delegate.reactiveUpsertAll( entities.length, entities ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java index c1015939c..0ebebe603 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java @@ -55,6 +55,7 @@ import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleIdEntityLoader; @@ -62,7 +63,6 @@ import org.hibernate.reactive.metamodel.mapping.internal.ReactiveCompoundNaturalIdMapping; import org.hibernate.reactive.metamodel.mapping.internal.ReactiveSimpleNaturalIdMapping; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.Update; @@ -359,7 +359,8 @@ else if ( result instanceof PersistentCollection ) { final PersistentCollection collection = (PersistentCollection) result; return collection.wasInitialized() ? completedFuture( (T) collection ) - : ( (ReactiveSession) session ).reactiveInitializeCollection( collection, false ) + : ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveInitializeCollection( collection, false ) .thenApply( v -> (T) result ); } else { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index 88cfe3ec6..dbb7a818f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -16,7 +16,6 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.UnknownProfileException; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.DeleteContext; @@ -59,8 +58,6 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveRemove(Object entity); - CompletionStage reactiveRemove(String entityName, boolean isCascadeDeleteEnabled, DeleteContext transientObjects); - CompletionStage reactiveRemove(String entityName, Object child, boolean isCascadeDeleteEnabled, DeleteContext transientEntities); CompletionStage reactiveMerge(T object); @@ -91,10 +88,6 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveFind(Class entityClass, Map naturalIds); - CompletionStage reactiveImmediateLoad(String entityName, Object id); - - CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing); - CompletionStage reactiveRemoveOrphanBeforeUpdates(String entityName, Object child); void setHibernateFlushMode(FlushMode flushMode); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java index e6d2aa1ee..006fbb63f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java @@ -46,18 +46,12 @@ public interface ReactiveStatelessSession extends ReactiveQueryProducer, Reactiv CompletionStage reactiveUpsert(Object entity); - CompletionStage reactiveUpsert(String entityName, Object entity); - CompletionStage reactiveUpsertAll(int batchSize, Object... entities); CompletionStage reactiveRefresh(Object entity); - CompletionStage reactiveRefresh(String entityName, Object entity); - CompletionStage reactiveRefresh(Object entity, LockMode lockMode); - CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode); - CompletionStage reactiveInsertAll(Object... entities); CompletionStage reactiveInsertAll(int batchSize, Object... entities); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java index 02b392a27..d86ffd7d1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java @@ -30,14 +30,12 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.AutoFlushEvent; import org.hibernate.event.spi.DeleteContext; import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.FlushEvent; import org.hibernate.event.spi.InitializeCollectionEvent; -import org.hibernate.event.spi.InitializeCollectionEventListener; import org.hibernate.event.spi.LoadEvent; import org.hibernate.event.spi.LoadEventListener; import org.hibernate.event.spi.LockEvent; @@ -826,8 +824,7 @@ public CompletionStage reactiveInitializeCollection(PersistentCollection eventListenerGroupInitCollection = getFactory().getEventListenerGroups().eventListenerGroup_INIT_COLLECTION; - return eventListenerGroupInitCollection + return getFactory().getEventListenerGroups().eventListenerGroup_INIT_COLLECTION .fireEventOnEachListener( event, (DefaultReactiveInitializeCollectionEventListener l) -> l::onReactiveInitializeCollection @@ -920,16 +917,6 @@ public CompletionStage reactiveRemove(Object entity) { return fireRemove( new DeleteEvent( entity, this ) ); } - @Override - public CompletionStage reactiveRemove( - String entityName, - boolean isCascadeDeleteEnabled, - DeleteContext transientEntities) - throws HibernateException { - // I'm not quite sure if we need this method - return reactiveRemove( entityName, null, isCascadeDeleteEnabled, transientEntities ); - } - @Override public CompletionStage reactiveRemove( String entityName, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java index 84483afb3..be9dc87b0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java @@ -7,6 +7,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.SessionException; import org.hibernate.UnknownEntityTypeException; import org.hibernate.UnresolvableObjectException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; @@ -14,6 +15,7 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; +import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -101,6 +103,7 @@ import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.loader.ast.spi.CascadingFetchProfile.REFRESH; import static org.hibernate.loader.internal.CacheLoadHelper.initializeCollectionFromCache; +import static org.hibernate.pretty.MessageHelper.collectionInfoString; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.id.impl.IdentifierGeneration.castToIdentifierType; @@ -590,18 +593,12 @@ public CompletionStage reactiveRefresh(Object entity) { return reactiveRefresh( bestGuessEntityName( entity ), entity, LockMode.NONE ); } - @Override - public CompletionStage reactiveRefresh(String entityName, Object entity) { - return reactiveRefresh( entityName, entity, LockMode.NONE ); - } - @Override public CompletionStage reactiveRefresh(Object entity, LockMode lockMode) { return reactiveRefresh( bestGuessEntityName( entity ), entity, lockMode ); } - @Override - public CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode) { + private CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode) { checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); final Object id = persister.getIdentifier( entity, this ); @@ -642,16 +639,7 @@ private CompletionStage fromInternalFetchProfile(CascadingFetchProfile cascad @Override public CompletionStage reactiveUpsert(Object entity) { checkOpen(); - return reactiveUpsert( null, entity ); - } - - /** - * @see StatelessSessionImpl#upsert(String, Object) - */ - @Override - public CompletionStage reactiveUpsert(String entityName, Object entity) { - checkOpen(); - final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); + final ReactiveEntityPersister persister = getEntityPersister( null, entity ); final Object id = idToUpsert( entity, persister ); final Object[] state = persister.getValues( entity ); if ( firePreUpsert( entity, id, state, persister ) ) { @@ -764,6 +752,15 @@ public CompletionStage reactiveInternalLoad(String entityName, Object id : completedFuture( object ); } + @Override + public CompletionStage reactiveImmediateLoad(String entityName, Object id) { + if ( persistenceContext.isLoadFinished() ) { + throw new SessionException( "proxies cannot be fetched by a stateless session" ); + } + // unless we are still in the process of handling a top-level load + return reactiveGet( entityName, id ); + } + @Override protected Object internalLoadGet(String entityName, Object id, PersistenceContext persistenceContext) { // otherwise immediately materialize it @@ -775,6 +772,44 @@ protected Object internalLoadGet(String entityName, Object id, PersistenceContex .whenComplete( (r, e) -> persistenceContext.afterLoad() ); } + @Override + public CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing) { + checkOpen(); + final CollectionEntry ce = persistenceContext.getCollectionEntry( collection ); + if ( ce == null ) { + throw new HibernateException( "no entry for collection" ); + } + if ( collection.wasInitialized() ) { + return voidFuture(); + } + else { + final ReactiveCollectionPersister loadedPersister = + (ReactiveCollectionPersister) ce.getLoadedPersister(); + final Object loadedKey = ce.getLoadedKey(); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Initializing collection " + + collectionInfoString( loadedPersister, collection, loadedKey, this ) ); + } + final boolean foundInCache = + initializeCollectionFromCache( loadedKey, loadedPersister, collection, this ); + if ( foundInCache ) { + LOG.trace( "Collection initialized from cache" ); + return voidFuture(); + } + else { + return loadedPersister.reactiveInitialize( loadedKey, this ) + .thenAccept( v -> { + handlePotentiallyEmptyCollection( collection, persistenceContext, loadedKey, loadedPersister ); + LOG.trace( "Collection initialized" ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.fetchCollection( loadedPersister.getRole() ); + } + } ); + } + } + } + @Override public CompletionStage reactiveFetch(T association, boolean unproxy) { checkOpen(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 981c2b703..7219501bc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1806,15 +1806,6 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ CompletionStage upsert(Object entity); - /** - * - * @param entityName The entityName for the entity to be merged - * @param entity a detached entity instance - * - * @see org.hibernate.StatelessSession#upsert(String, Object) - */ - CompletionStage upsert(String entityName, Object entity); - /** * Use a SQL {@code merge into} statement to perform * an upsert on multiple rows using the size of the given array diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index 32227de7b..da09a284b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -19,8 +19,6 @@ import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.engine.ReactiveActionQueue; import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveConnectionSupplier; @@ -43,7 +41,6 @@ import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; -import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -59,8 +56,6 @@ */ public class StageSessionImpl implements Stage.Session { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final ReactiveSession delegate; public StageSessionImpl(ReactiveSession session) { @@ -252,38 +247,22 @@ public CompletionStage lock(Object entity, LockOptions lockOptions) { @Override public FlushMode getFlushMode() { - switch ( delegate.getHibernateFlushMode() ) { - case MANUAL: - return FlushMode.MANUAL; - case COMMIT: - return FlushMode.COMMIT; - case AUTO: - return FlushMode.AUTO; - case ALWAYS: - return FlushMode.ALWAYS; - default: - throw LOG.impossibleFlushModeIllegalState(); - } + return switch ( delegate.getHibernateFlushMode() ) { + case MANUAL -> FlushMode.MANUAL; + case COMMIT -> FlushMode.COMMIT; + case AUTO -> FlushMode.AUTO; + case ALWAYS -> FlushMode.ALWAYS; + }; } @Override public Stage.Session setFlushMode(FlushMode flushMode) { - switch ( flushMode ) { - case COMMIT: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.COMMIT ); - break; - case AUTO: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.AUTO ); - break; - case MANUAL: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.MANUAL ); - break; - case ALWAYS: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.ALWAYS ); - break; - default: - throw new IllegalArgumentException( "Unsupported flushMode: " + flushMode ); - } + delegate.setHibernateFlushMode( switch ( flushMode ) { + case COMMIT -> org.hibernate.FlushMode.COMMIT; + case AUTO -> org.hibernate.FlushMode.AUTO; + case MANUAL -> org.hibernate.FlushMode.MANUAL; + case ALWAYS -> org.hibernate.FlushMode.ALWAYS; + } ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 4c325fdf9..07f7ef35a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -154,11 +154,6 @@ public CompletionStage upsert(Object entity) { return delegate.reactiveUpsert( entity ); } - @Override - public CompletionStage upsert(String entityName, Object entity) { - return delegate.reactiveUpsert( entityName, entity ); - } - @Override public CompletionStage upsertAll(Object... entities) { return delegate.reactiveUpsertAll( entities.length, entities ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java index 52dc52a72..8dcc07a6a 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java @@ -104,34 +104,6 @@ public void testMutinyUpsert(VertxTestContext context) { ); } - @Test - public void testMutinyUpsertWithEntityName(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "hello earth" ) ) - .call( () -> ss.upsert( Record.class.getName(), new Record( 456L, "hello mars" ) ) ) - .invoke( this::assertQueries ) - ) - .call( v -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .invoke( results -> assertThat( results ).containsExactly( - new Record( 123L, "hello earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - .call( () -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "goodbye earth" ) ) - ) ) - .invoke( this::assertQueries ) - .call( v -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .invoke( results -> assertThat( results ).containsExactly( - new Record( 123L, "goodbye earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - ); - } - @Test public void testStageUpsert(VertxTestContext context) { test( context, getSessionFactory().withStatelessTransaction( ss -> ss @@ -160,34 +132,6 @@ public void testStageUpsert(VertxTestContext context) { ); } - @Test - public void testStageUpsertWithEntityName(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "hello earth" ) ) - .thenCompose( v -> ss.upsert( Record.class.getName(), new Record( 456L, "hello mars" ) ) ) - ) - .thenAccept( v -> this.assertQueries() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .thenAccept( results -> assertThat( results ).containsExactly( - new Record( 123L, "hello earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "goodbye earth" ) ) - ) ) - .thenAccept( v -> this.assertQueries() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .thenAccept( results -> assertThat( results ).containsExactly( - new Record( 123L, "goodbye earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - ); - } - private void assertQueries() { if ( hasMergeOperator() ) { assertThat( sqlTracker.getLoggedQueries() ).have( IS_USING_MERGE );