From bace0f4d4b3dc42d74b7452d58e2b2bfefc3d056 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 14 Aug 2025 18:28:37 +0200 Subject: [PATCH 1/2] Remove warnings in BaseReactiveTest Replace class casts with pattern variables --- .../hibernate/reactive/BaseReactiveTest.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java index 508625bcb..223b46fc0 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java @@ -293,38 +293,32 @@ protected CompletionStage cleanDb() { } protected static CompletionStage closeSession(Object closable) { - if ( closable instanceof CompletionStage ) { - CompletionStage closableStage = (CompletionStage) closable; + if ( closable instanceof CompletionStage closableStage ) { return closableStage.thenCompose( BaseReactiveTest::closeSession ); } - if ( closable instanceof Uni ) { - Uni closableUni = (Uni) closable; + if ( closable instanceof Uni closableUni ) { return closableUni.subscribeAsCompletionStage() .thenCompose( BaseReactiveTest::closeSession ); } - if ( closable instanceof ReactiveConnection ) { - return ( (ReactiveConnection) closable ).close(); + if ( closable instanceof ReactiveConnection reactiveConnection) { + return reactiveConnection.close(); } - if ( closable instanceof Mutiny.Session ) { - Mutiny.Session mutiny = (Mutiny.Session) closable; + if ( closable instanceof Mutiny.Session mutiny ) { if ( mutiny.isOpen() ) { return mutiny.close().subscribeAsCompletionStage(); } } - if ( closable instanceof Stage.Session ) { - Stage.Session stage = (Stage.Session) closable; + if ( closable instanceof Stage.Session stage ) { if ( stage.isOpen() ) { return stage.close(); } } - if ( closable instanceof Mutiny.StatelessSession ) { - Mutiny.StatelessSession mutiny = (Mutiny.StatelessSession) closable; + if ( closable instanceof Mutiny.StatelessSession mutiny ) { if ( mutiny.isOpen() ) { return mutiny.close().subscribeAsCompletionStage(); } } - if ( closable instanceof Stage.StatelessSession ) { - Stage.StatelessSession stage = (Stage.StatelessSession) closable; + if ( closable instanceof Stage.StatelessSession stage ) { if ( stage.isOpen() ) { return stage.close(); } From 7ae523ad53179f3e014e7063dac879cd036215b8 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 14 Aug 2025 18:31:38 +0200 Subject: [PATCH 2/2] [#2330] Test collection events with StatelessSession Add test from ORM issue HHH-19523 --- ...ollectionStatelessSessionListenerTest.java | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/CollectionStatelessSessionListenerTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CollectionStatelessSessionListenerTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CollectionStatelessSessionListenerTest.java new file mode 100644 index 000000000..0d88a6700 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CollectionStatelessSessionListenerTest.java @@ -0,0 +1,245 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.AbstractCollectionEvent; +import org.hibernate.event.spi.EventType; +import org.hibernate.event.spi.PostCollectionRecreateEvent; +import org.hibernate.event.spi.PostCollectionRecreateEventListener; +import org.hibernate.event.spi.PostCollectionRemoveEvent; +import org.hibernate.event.spi.PostCollectionRemoveEventListener; +import org.hibernate.event.spi.PreCollectionRecreateEvent; +import org.hibernate.event.spi.PreCollectionRecreateEventListener; +import org.hibernate.event.spi.PreCollectionRemoveEvent; +import org.hibernate.event.spi.PreCollectionRemoveEventListener; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Adapt test in ORM: CollectionStatelessSessionListenerTest + */ +public class CollectionStatelessSessionListenerTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return Set.of( EntityA.class, EntityB.class ); + } + + @Test + public void mutinyStatelessInsert(VertxTestContext context) { + final List events = new ArrayList<>(); + initializeListeners( events ); + + EntityA a = new EntityA(); + EntityB b = new EntityB(); + a.children.add( b ); + test( context, getMutinySessionFactory() + .withStatelessTransaction( statelessSession -> statelessSession + .insert( b ) + .chain( () -> statelessSession.insert( a ) ) + .chain( () -> statelessSession.delete( a ) ) + .chain( () -> statelessSession.delete( b ) ) + ) + .invoke( () -> assertEvents( events ) ) + ); + } + + @Test + public void mutinyStatelessInsertAll(VertxTestContext context) { + final List events = new ArrayList<>(); + initializeListeners( events ); + + EntityA a = new EntityA(); + EntityB b = new EntityB(); + a.children.add( b ); + test( context, getMutinySessionFactory() + .withStatelessTransaction( statelessSession -> statelessSession + .insertAll( b, a ) + .chain( () -> statelessSession.deleteAll( a, b ) ) + ) + .invoke( () -> assertEvents( events ) ) + ); + } + + @Test + public void stageStatelessInsert(VertxTestContext context) { + final List events = new ArrayList<>(); + initializeListeners( events ); + + EntityA a = new EntityA(); + EntityB b = new EntityB(); + a.children.add( b ); + test( context, getSessionFactory() + .withStatelessTransaction( statelessSession -> statelessSession + .insert( b ) + .thenCompose( v -> statelessSession.insert( a ) ) + .thenCompose( v -> statelessSession.delete( a ) ) + .thenCompose( v -> statelessSession.delete( b ) ) + ) + .thenAccept( v -> assertEvents( events ) ) + ); + } + + @Test + public void stageStatelessInsertAll(VertxTestContext context) { + final List events = new ArrayList<>(); + initializeListeners( events ); + + EntityA a = new EntityA(); + EntityB b = new EntityB(); + a.children.add( b ); + test( context, getSessionFactory() + .withStatelessTransaction( statelessSession -> statelessSession + .insert( b, a ) + .thenCompose( v -> statelessSession.delete( a, b ) ) + ) + .thenAccept( v -> assertEvents( events ) ) + ); + } + + private static void assertEvents(List events) { + assertThat( events ).hasSize( 4 ); + assertThat( events.get( 0 ) ) + .isInstanceOf( PreCollectionRecreateEvent.class ) + .extracting( AbstractCollectionEvent::getAffectedOwnerEntityName ).isEqualTo( EntityA.class.getName() ); + assertThat( events.get( 1 ) ) + .isInstanceOf( PostCollectionRecreateEvent.class ) + .extracting( AbstractCollectionEvent::getAffectedOwnerEntityName ).isEqualTo( EntityA.class.getName() ); + assertThat( events.get( 2 ) ) + .isInstanceOf( PreCollectionRemoveEvent.class ) + .extracting( AbstractCollectionEvent::getAffectedOwnerEntityName ).isEqualTo( EntityA.class.getName() ); + assertThat( events.get( 3 ) ) + .isInstanceOf( PostCollectionRemoveEvent.class ) + .extracting( AbstractCollectionEvent::getAffectedOwnerEntityName ).isEqualTo( EntityA.class.getName() ); + } + + private void initializeListeners(List events) { + final EventListenerRegistry registry = ( (SessionFactoryImplementor) factoryManager + .getHibernateSessionFactory() ) + .getEventListenerRegistry(); + + // Clear previous listeners + registry.getEventListenerGroup( EventType.PRE_COLLECTION_RECREATE ) + .clearListeners(); + registry.getEventListenerGroup( EventType.PRE_COLLECTION_REMOVE ) + .clearListeners(); + registry.getEventListenerGroup( EventType.POST_COLLECTION_RECREATE ) + .clearListeners(); + registry.getEventListenerGroup( EventType.POST_COLLECTION_REMOVE ) + .clearListeners(); + + // Add new listeners + registry.getEventListenerGroup( EventType.PRE_COLLECTION_RECREATE ) + .appendListener( new MyPreCollectionRecreateEventListener( events ) ); + registry.getEventListenerGroup( EventType.PRE_COLLECTION_REMOVE ) + .appendListener( new MyPreCollectionRemoveEventListener( events ) ); + registry.getEventListenerGroup( EventType.POST_COLLECTION_RECREATE ) + .appendListener( new MyPostCollectionRecreateEventListener( events ) ); + registry.getEventListenerGroup( EventType.POST_COLLECTION_REMOVE ) + .appendListener( new MyPostCollectionRemoveEventListener( events ) ); + } + + @Entity + @Table(name = "ENTITY_A") + public static class EntityA { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "ID") + Integer id; + + @OneToMany + @JoinColumn(name = "ENTITY_A") + Collection children = new ArrayList<>(); + } + + @Entity + @Table(name = "ENTITY_B") + public static class EntityB { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "ID") + Integer id; + } + + public static class MyPreCollectionRecreateEventListener implements PreCollectionRecreateEventListener { + + private final List events; + + + public MyPreCollectionRecreateEventListener(List events) { + this.events = events; + } + + @Override + public void onPreRecreateCollection(PreCollectionRecreateEvent event) { + events.add( event ); + } + + } + + public static class MyPreCollectionRemoveEventListener implements PreCollectionRemoveEventListener { + + private final List events; + + public MyPreCollectionRemoveEventListener(List events) { + this.events = events; + } + + @Override + public void onPreRemoveCollection(PreCollectionRemoveEvent event) { + events.add( event ); + } + + } + + public static class MyPostCollectionRecreateEventListener implements PostCollectionRecreateEventListener { + + private final List events; + + public MyPostCollectionRecreateEventListener(List events) { + this.events = events; + } + + @Override + public void onPostRecreateCollection(PostCollectionRecreateEvent event) { + events.add( event ); + } + } + + public static class MyPostCollectionRemoveEventListener implements PostCollectionRemoveEventListener { + + private final List events; + + public MyPostCollectionRemoveEventListener(List events) { + this.events = events; + } + + @Override + public void onPostRemoveCollection(PostCollectionRemoveEvent event) { + events.add( event ); + } + } +}