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.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventManager;
import org.hibernate.event.spi.DeleteContext;
import org.hibernate.event.spi.EventSource;
@ -397,6 +398,17 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
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
public void afterOperation(boolean success) {
delegate.afterOperation( success );

View File

@ -18,6 +18,7 @@ import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.StatelessSession;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventSource;
import org.hibernate.graph.GraphSemantic;
import org.hibernate.query.Query;
@ -537,6 +538,14 @@ public interface SharedSessionContractImplementor
*/
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}?
*

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.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventManager;
import org.hibernate.graph.RootGraph;
import org.hibernate.jdbc.ReturningWork;
@ -558,6 +559,17 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl
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
public void afterOperation(boolean success) {
delegate.afterOperation( success );

View File

@ -74,6 +74,29 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
final EventSource session = event.getSession();
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() );
prepareEntityFlushes( session, persistenceContext );
@ -84,21 +107,6 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
// now, any collections that are initialized
// inside this block do not get updated - they
// 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) {

View File

@ -48,8 +48,13 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener
// 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).
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();
flushEverythingToExecutions( event );
flushEverythingToExecutions( event, persistenceContext, session );
if ( flushIsReallyNeeded( event, source ) ) {
LOG.trace( "Need to execute flush" );
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) {
return source.getHibernateFlushMode() == FlushMode.ALWAYS
|| source.getActionQueue().areTablesToBeUpdated( event.getQuerySpaces() );

View File

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

View File

@ -7,6 +7,7 @@
package org.hibernate.event.spi;
import org.hibernate.HibernateException;
import org.hibernate.SharedSessionContract;
/**
* 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.
*/
void onAutoFlush(AutoFlushEvent event) throws HibernateException;
default void onAutoPreFlush(EventSource source) {
}
}

View File

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

View File

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

View File

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