From 257ce5cbbe6727e1fbd20c9fb09ec61867527fd6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 1 May 2025 08:08:06 +0200 Subject: [PATCH] fixes before release 1. fix some more incorrect casts to ReactiveSession or ReactiveSessionImpl 2. remove the SS.upsert() method taking an entity name (we don't need it) 3. some minor code cleanups in EntityTypes and CollectionTypes --- .../ReactivePersistenceContextAdapter.java | 7 +- .../reactive/engine/impl/CollectionTypes.java | 200 +++++++++--------- .../reactive/engine/impl/EntityTypes.java | 20 +- ...ctiveSharedSessionContractImplementor.java | 5 + ...tiveInitializeCollectionEventListener.java | 29 +-- .../org/hibernate/reactive/mutiny/Mutiny.java | 12 -- .../mutiny/impl/MutinySessionImpl.java | 38 ++-- .../impl/MutinyStatelessSessionImpl.java | 5 - .../impl/ReactiveAbstractEntityPersister.java | 5 +- .../reactive/session/ReactiveSession.java | 7 - .../session/ReactiveStatelessSession.java | 6 - .../session/impl/ReactiveSessionImpl.java | 15 +- .../impl/ReactiveStatelessSessionImpl.java | 69 ++++-- .../org/hibernate/reactive/stage/Stage.java | 9 - .../reactive/stage/impl/StageSessionImpl.java | 45 ++-- .../stage/impl/StageStatelessSessionImpl.java | 5 - .../org/hibernate/reactive/UpsertTest.java | 56 ----- 17 files changed, 206 insertions(+), 327 deletions(-) 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 );