Non-reactive forceFlush in AbstractReactiveSaveEventListener.java:172
There is a call to non-reactive forceFlush of SessionImpl that leads to UnsupportedOperationException from DefaultReactiveFlushEventListener:
Caused by: java.util.concurrent.CompletionException: javax.persistence.PersistenceException: org.hibernate.HibernateException: java.lang.UnsupportedOperationException
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1194)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:144)
at org.hibernate.reactive.event.impl.AbstractReactiveSaveEventListener.reactiveSaveWithGeneratedId(AbstractReactiveSaveEventListener.java:123)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.saveTransientEntity(DefaultReactiveMergeEventListener.java:283)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.lambda$entityIsTransient$5(DefaultReactiveMergeEventListener.java:257)
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:144)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.entityIsTransient(DefaultReactiveMergeEventListener.java:257)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.lambda$entityIsDetached$11(DefaultReactiveMergeEventListener.java:364)
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:144)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.entityIsDetached(DefaultReactiveMergeEventListener.java:321)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.reactiveOnMerge(DefaultReactiveMergeEventListener.java:190)
at org.hibernate.reactive.event.impl.DefaultReactiveMergeEventListener.reactiveOnMerge(DefaultReactiveMergeEventListener.java:90)
at org.hibernate.event.service.internal.EventListenerGroupImpl.lambda$fireEventOnEachListener$0(EventListenerGroupImpl.java:133)
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:144)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:133)
at org.hibernate.reactive.session.impl.ReactiveSessionImpl.fireMerge(ReactiveSessionImpl.java:849)
... 298 more
Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: java.lang.UnsupportedOperationException
at app//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at app//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at app//org.hibernate.reactive.session.impl.ReactiveExceptionConverter.convert(ReactiveExceptionConverter.java:31)
at app//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1411)
at app//org.hibernate.internal.SessionImpl.forceFlush(SessionImpl.java:1438)
at app//org.hibernate.reactive.event.impl.AbstractReactiveSaveEventListener.reactivePerformSave(AbstractReactiveSaveEventListener.java:172)
at app//org.hibernate.reactive.event.impl.AbstractReactiveSaveEventListener.lambda$reactiveSaveWithGeneratedId$0(AbstractReactiveSaveEventListener.java:123)
at [email protected]/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
... 320 more
Caused by: org.hibernate.HibernateException: java.lang.UnsupportedOperationException
... 326 more
Caused by: java.lang.UnsupportedOperationException
at org.hibernate.reactive.event.impl.DefaultReactiveFlushEventListener.onFlush(DefaultReactiveFlushEventListener.java:63)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1407)
... 324 more
maresja1 can you add a reproducer and/or test scenario with entities?
@blafond I understand the request, but in my situation that would be pretty difficult. I thought that the call to forceFlush on SessionImpl is plainly wrong, as it results in UnsupportedOperationException from DefaultReactiveFlushEventListener.
In other words code from AbstractReactiveSaveEventListener ends up calling non-reactive flush, that ends up with expected exception from DefaultReactiveFlushEventListener.
I am working on experimental integration between Hibernate Reactive and Spring Data. In simple unit test it works fine, when trying to migrate existing non-reactive service to this integration I bumped into this exception. Reproducing it in a "clear" (non-Spring) environment would take long and might end up nowhere - as even this manifestation seems to be affected by reordering, in other words, the tests sometimes fail and sometimes pass.
Let it be noted, that this issue has a workaround and that is to flush reactively, manually after delete. As it is probably obvious from the code, the wrong kind of flush happens only when there is entity in the persistent context, which state is DELETED.
Feel free to ask for more details.
Yes, this is a bug. It shouldn't be possible to reach that code.
Been digging around to try and find a proper test case or scenario.
The error is being thrown via the reactive AbstractReactiveSaveEventListener.reactivePerformSave() which basically contains the same code as ORM's AbstractSaveEventListener.performSave()
The only test in hibernate-core that enters that if( old != null ) check is via DetachedBagDelayedOperationTest.testCollectionWithQueuedOperationsOnRollback()
@DavideD, @gavinking Either of you know from the user's description how his use-case might be set up? else I'll try and recreate this ORM test in reactive.
The only test in hibernate-core that enters that if( old != null ) check is via DetachedBagDelayedOperationTest.testCollectionWithQueuedOperationsOnRollback()
Sounds good, can't you adapt the same test for Hibernate Reactive?
@DavideD, @gavinking Either of you know from the user's description how his use-case might be set up? else I'll try and recreate this ORM test in reactive.
At this point it doesn't really matter the exact use case. The call to the non reactive method is wrong anyway. So it's fine to adapt the test from ORM.
After further testing in ORM, there are a number of tests that create a DELETED entity status where the entity is "old", however, I can't find a test/scenario where the "old" entity's status is DELETED (see: AbstractSaveEventListener.performSave())
So still have not been able to reproduce this in Reactive or find a test to port in ORM.
@blafond, this should show the exception:
@Test
public void testForceFlushWithDelete(TestContext context) {
final Flour rose1 = new Flour( 2, "Rose1", "1", "Full fragrance" );
final Flour rose2 = new Flour( 2, "Rose2", "2", "Full fragrance" );
test( context, getMutinySessionFactory()
.withTransaction( session -> session
.persist( rose1 )
.call( () -> session.remove( rose1 ) )
.call( () -> session.persist( rose2 ) )
)
);
}
I've used the entity in ORMReactivePersistenceTest, but, please, find a better place for it (maybe you can adapt it for MutinySessionTest).
I ran your in test with an ORM test in hibernate-core
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: org.hibernate.test.any.Person
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:120)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:756)
... 17 more
Your test ends up with something similar...
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: org.hibernate.reactive.ORMForceFlushTest$Flour
at app//org.hibernate.reactive.event.impl.DefaultReactivePersistEventListener.reactiveOnPersist(DefaultReactivePersistEventListener.java:121)
at app//org.hibernate.reactive.event.impl.DefaultReactivePersistEventListener.reactiveOnPersist(DefaultReactivePersistEventListener.java:58)
at app//org.hibernate.event.service.internal.EventListenerGroupImpl.lambda$fireEventOnEachListener$0(EventListenerGroupImpl.java:133)
at org.hibernate.reactive.session.impl.ReactiveSessionImpl.firePersist(ReactiveSessionImpl.java:699)
The user's exception is thrown from Reactive is thrown from the same ORM class: EventListenerGroupImpl.fireEventOnEachListener()' but triggered via doFlush()`
If you don't show me how you have adapted the test, I cannot help you
@DavideD The commits above fixes the issue. I still need to refactor the reactivePerformSave() to simplify it.