HHH-17073 Auto flush broken when using sequence generator ID

This commit is contained in:
Andrea Boriero 2024-02-27 17:02:13 +01:00 committed by Andrea Boriero
parent 7a4523a470
commit fe77bcfee9
10 changed files with 113 additions and 23 deletions

View File

@ -38,6 +38,7 @@ import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventManager; import org.hibernate.event.spi.EventManager;
import org.hibernate.event.spi.DeleteContext; import org.hibernate.event.spi.DeleteContext;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
@ -397,6 +398,17 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate.autoFlushIfRequired( querySpaces ); return delegate.autoFlushIfRequired( querySpaces );
} }
@Override
public boolean autoFlushIfRequired(Set<String> querySpaces, boolean skipPreFlush)
throws HibernateException {
return delegate.autoFlushIfRequired( querySpaces, skipPreFlush );
}
@Override
public void autoPreFlush() {
delegate.autoPreFlush();
}
@Override @Override
public void afterOperation(boolean success) { public void afterOperation(boolean success) {
delegate.afterOperation( success ); delegate.afterOperation( success );

View File

@ -18,6 +18,7 @@ import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.StatelessSession; import org.hibernate.StatelessSession;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.GraphSemantic;
import org.hibernate.query.Query; import org.hibernate.query.Query;
@ -537,6 +538,14 @@ public interface SharedSessionContractImplementor
*/ */
boolean autoFlushIfRequired(Set<String> querySpaces) throws HibernateException; boolean autoFlushIfRequired(Set<String> querySpaces) throws HibernateException;
default boolean autoFlushIfRequired(Set<String> querySpaces, boolean skipPreFlush)
throws HibernateException {
return autoFlushIfRequired( querySpaces );
}
default void autoPreFlush(){
}
/** /**
* Are we currently enforcing a {@linkplain GraphSemantic#FETCH fetch graph}? * Are we currently enforcing a {@linkplain GraphSemantic#FETCH fetch graph}?
* *

View File

@ -26,6 +26,7 @@ import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventManager; import org.hibernate.event.spi.EventManager;
import org.hibernate.graph.RootGraph; import org.hibernate.graph.RootGraph;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.ReturningWork;
@ -558,6 +559,17 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl
return delegate.autoFlushIfRequired( querySpaces ); return delegate.autoFlushIfRequired( querySpaces );
} }
@Override
public boolean autoFlushIfRequired(Set<String> querySpaces, boolean skipPreFlush)
throws HibernateException {
return delegate.autoFlushIfRequired( querySpaces, skipPreFlush );
}
@Override
public void autoPreFlush() {
delegate.autoPreFlush();
}
@Override @Override
public void afterOperation(boolean success) { public void afterOperation(boolean success) {
delegate.afterOperation( success ); delegate.afterOperation( success );

View File

@ -74,6 +74,29 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
final EventSource session = event.getSession(); final EventSource session = event.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
preFlush( session, persistenceContext );
flushEverythingToExecutions( event, persistenceContext, session );
}
protected void flushEverythingToExecutions(FlushEvent event, PersistenceContext persistenceContext, EventSource session) {
persistenceContext.setFlushing( true );
try {
int entityCount = flushEntities( event, persistenceContext );
int collectionCount = flushCollections( session, persistenceContext );
event.setNumberOfEntitiesProcessed( entityCount );
event.setNumberOfCollectionsProcessed( collectionCount );
}
finally {
persistenceContext.setFlushing( false);
}
//some statistics
logFlushResults( event );
}
protected void preFlush(EventSource session, PersistenceContext persistenceContext) {
session.getInterceptor().preFlush( persistenceContext.managedEntitiesIterator() ); session.getInterceptor().preFlush( persistenceContext.managedEntitiesIterator() );
prepareEntityFlushes( session, persistenceContext ); prepareEntityFlushes( session, persistenceContext );
@ -84,21 +107,6 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
// now, any collections that are initialized // now, any collections that are initialized
// inside this block do not get updated - they // inside this block do not get updated - they
// are ignored until the next flush // are ignored until the next flush
persistenceContext.setFlushing( true );
try {
int entityCount = flushEntities( event, persistenceContext );
int collectionCount = flushCollections( session, persistenceContext );
event.setNumberOfEntitiesProcessed( entityCount );
event.setNumberOfCollectionsProcessed( collectionCount );
}
finally {
persistenceContext.setFlushing(false);
}
//some statistics
logFlushResults( event );
} }
protected void logFlushResults(FlushEvent event) { protected void logFlushResults(FlushEvent event) {

View File

@ -48,8 +48,13 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener
// Need to get the number of collection removals before flushing to executions // Need to get the number of collection removals before flushing to executions
// (because flushing to executions can add collection removal actions to the action queue). // (because flushing to executions can add collection removal actions to the action queue).
final ActionQueue actionQueue = source.getActionQueue(); final ActionQueue actionQueue = source.getActionQueue();
final EventSource session = event.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( !event.isSkipPreFlush() ) {
preFlush( session, persistenceContext );
}
final int oldSize = actionQueue.numberOfCollectionRemovals(); final int oldSize = actionQueue.numberOfCollectionRemovals();
flushEverythingToExecutions( event ); flushEverythingToExecutions( event, persistenceContext, session );
if ( flushIsReallyNeeded( event, source ) ) { if ( flushIsReallyNeeded( event, source ) ) {
LOG.trace( "Need to execute flush" ); LOG.trace( "Need to execute flush" );
event.setFlushRequired( true ); event.setFlushRequired( true );
@ -87,6 +92,13 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener
} }
} }
@Override
public void onAutoPreFlush(EventSource source) {
if ( flushMightBeNeeded( source ) ) {
preFlush( source, source.getPersistenceContextInternal() );
}
}
private boolean flushIsReallyNeeded(AutoFlushEvent event, final EventSource source) { private boolean flushIsReallyNeeded(AutoFlushEvent event, final EventSource source) {
return source.getHibernateFlushMode() == FlushMode.ALWAYS return source.getHibernateFlushMode() == FlushMode.ALWAYS
|| source.getActionQueue().areTablesToBeUpdated( event.getQuerySpaces() ); || source.getActionQueue().areTablesToBeUpdated( event.getQuerySpaces() );

View File

@ -16,10 +16,16 @@ public class AutoFlushEvent extends FlushEvent {
private Set<String> querySpaces; private Set<String> querySpaces;
private boolean flushRequired; private boolean flushRequired;
private boolean skipPreFlush;
public AutoFlushEvent(Set<String> querySpaces, EventSource source) { public AutoFlushEvent(Set<String> querySpaces, EventSource source) {
super(source); this( querySpaces, false, source );
}
public AutoFlushEvent(Set<String> querySpaces, boolean skipPreFlush, EventSource source) {
super( source );
this.querySpaces = querySpaces; this.querySpaces = querySpaces;
this.skipPreFlush = skipPreFlush;
} }
public Set<String> getQuerySpaces() { public Set<String> getQuerySpaces() {
@ -37,4 +43,8 @@ public class AutoFlushEvent extends FlushEvent {
public void setFlushRequired(boolean dirty) { public void setFlushRequired(boolean dirty) {
this.flushRequired = dirty; this.flushRequired = dirty;
} }
public boolean isSkipPreFlush() {
return skipPreFlush;
}
} }

View File

@ -7,6 +7,7 @@
package org.hibernate.event.spi; package org.hibernate.event.spi;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.SharedSessionContract;
/** /**
* Defines the contract for handling of session auto-flush events. * Defines the contract for handling of session auto-flush events.
@ -20,4 +21,7 @@ public interface AutoFlushEventListener {
* @param event The auto-flush event to be handled. * @param event The auto-flush event to be handled.
*/ */
void onAutoFlush(AutoFlushEvent event) throws HibernateException; void onAutoFlush(AutoFlushEvent event) throws HibernateException;
default void onAutoPreFlush(EventSource source) {
}
} }

View File

@ -1359,17 +1359,36 @@ public class SessionImpl
@Override @Override
public boolean autoFlushIfRequired(Set<String> querySpaces) throws HibernateException { public boolean autoFlushIfRequired(Set<String> querySpaces) throws HibernateException {
return autoFlushIfRequired( querySpaces, false );
}
@Override
public boolean autoFlushIfRequired(Set<String> querySpaces, boolean skipPreFlush)
throws HibernateException {
checkOpen(); checkOpen();
if ( !isTransactionInProgress() ) { if ( !isTransactionInProgress() ) {
// do not auto-flush while outside a transaction // do not auto-flush while outside a transaction
return false; return false;
} }
AutoFlushEvent event = new AutoFlushEvent( querySpaces, this ); AutoFlushEvent event = new AutoFlushEvent( querySpaces, skipPreFlush, this );
fastSessionServices.eventListenerGroup_AUTO_FLUSH fastSessionServices.eventListenerGroup_AUTO_FLUSH
.fireEventOnEachListener( event, AutoFlushEventListener::onAutoFlush ); .fireEventOnEachListener( event, AutoFlushEventListener::onAutoFlush );
return event.isFlushRequired(); return event.isFlushRequired();
} }
@Override
public void autoPreFlush(){
checkOpen();
if ( !isTransactionInProgress() ) {
// do not auto-flush while outside a transaction
return;
}
fastSessionServices.eventListenerGroup_AUTO_FLUSH
.fireEventOnEachListener( this, AutoFlushEventListener::onAutoPreFlush );
}
@Override @Override
public boolean isDirty() throws HibernateException { public boolean isDirty() throws HibernateException {
checkOpen(); checkOpen();

View File

@ -30,6 +30,7 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.Generator; import org.hibernate.generator.Generator;
import org.hibernate.generator.values.GeneratedValues; import org.hibernate.generator.values.GeneratedValues;

View File

@ -12,12 +12,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import jakarta.persistence.Tuple; import jakarta.persistence.Tuple;
import org.hibernate.InstantiationException; import org.hibernate.InstantiationException;
import org.hibernate.ScrollMode; import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.internal.EmptyScrollableResults; import org.hibernate.internal.EmptyScrollableResults;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.Query; import org.hibernate.query.Query;
@ -105,7 +107,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
JdbcParametersList.empty(), JdbcParametersList.empty(),
jdbcParameterBindings jdbcParameterBindings
); );
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
return session.getFactory().getJdbcServices().getJdbcSelectExecutor().executeQuery( return session.getFactory().getJdbcServices().getJdbcSelectExecutor().executeQuery(
jdbcSelect, jdbcSelect,
jdbcParameterBindings, jdbcParameterBindings,
@ -133,8 +135,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
JdbcParametersList.empty(), JdbcParametersList.empty(),
jdbcParameterBindings jdbcParameterBindings
); );
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames() );
return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
jdbcSelect, jdbcSelect,
jdbcParameterBindings, jdbcParameterBindings,
@ -162,7 +163,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
final JdbcSelectExecutor jdbcSelectExecutor = session.getFactory() final JdbcSelectExecutor jdbcSelectExecutor = session.getFactory()
.getJdbcServices() .getJdbcServices()
.getJdbcSelectExecutor(); .getJdbcSelectExecutor();
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
return jdbcSelectExecutor.scroll( return jdbcSelectExecutor.scroll(
jdbcSelect, jdbcSelect,
scrollMode, scrollMode,
@ -204,7 +205,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
return selections.size() == 1 ? selections.get( 0 ) : null; return selections.size() == 1 ? selections.get( 0 ) : null;
} }
private static final Map<Class<?>,Class<?>> WRAPPERS private static final Map<Class<?>, Class<?>> WRAPPERS
= Map.of( = Map.of(
boolean.class, Boolean.class, boolean.class, Boolean.class,
int.class, Integer.class, int.class, Integer.class,
@ -318,6 +319,8 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
CacheableSqmInterpretation localCopy = cacheableSqmInterpretation; CacheableSqmInterpretation localCopy = cacheableSqmInterpretation;
JdbcParameterBindings jdbcParameterBindings = null; JdbcParameterBindings jdbcParameterBindings = null;
executionContext.getSession().autoPreFlush();
if ( localCopy == null ) { if ( localCopy == null ) {
synchronized ( this ) { synchronized ( this ) {
localCopy = cacheableSqmInterpretation; localCopy = cacheableSqmInterpretation;