From d4e1b7ffbcb5f92901b57cbb5d288915a4af5c7c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sun, 11 Aug 2019 21:18:34 +0100 Subject: [PATCH 01/99] HHH-13561 Do not retrieve the same ActionQueue multiple times --- .../internal/DefaultDirtyCheckEventListener.java | 8 +++++--- .../event/internal/DefaultRefreshEventListener.java | 12 +++++++++--- .../java/org/hibernate/internal/SessionImpl.java | 3 ++- .../org/hibernate/test/tm/AfterCompletionTest.java | 6 ++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java index 5e42eb1cbc..7f28bb1b7a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java @@ -7,6 +7,7 @@ package org.hibernate.event.internal; import org.hibernate.HibernateException; +import org.hibernate.engine.spi.ActionQueue; import org.hibernate.event.spi.DirtyCheckEvent; import org.hibernate.event.spi.DirtyCheckEventListener; import org.hibernate.internal.CoreLogging; @@ -29,11 +30,12 @@ public class DefaultDirtyCheckEventListener extends AbstractFlushingEventListene * @throws HibernateException */ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException { - int oldSize = event.getSession().getActionQueue().numberOfCollectionRemovals(); + final ActionQueue actionQueue = event.getSession().getActionQueue(); + int oldSize = actionQueue.numberOfCollectionRemovals(); try { flushEverythingToExecutions(event); - boolean wasNeeded = event.getSession().getActionQueue().hasAnyQueuedActions(); + boolean wasNeeded = actionQueue.hasAnyQueuedActions(); if ( wasNeeded ) { LOG.debug( "Session dirty" ); } @@ -43,7 +45,7 @@ public class DefaultDirtyCheckEventListener extends AbstractFlushingEventListene event.setDirty( wasNeeded ); } finally { - event.getSession().getActionQueue().clearFromFlushNeededCheck( oldSize ); + actionQueue.clearFromFlushNeededCheck( oldSize ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index 0c0f2624fb..9b8b9c41b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -20,15 +20,18 @@ import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; +import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.RefreshEvent; import org.hibernate.event.spi.RefreshEventListener; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.metamodel.spi.MetamodelImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; @@ -239,20 +242,23 @@ public class DefaultRefreshEventListener implements RefreshEventListener { private void evictCachedCollections(Type[] types, Serializable id, EventSource source) throws HibernateException { + final ActionQueue actionQueue = source.getActionQueue(); + final SessionFactoryImplementor factory = source.getFactory(); + final MetamodelImplementor metamodel = factory.getMetamodel(); for ( Type type : types ) { if ( type.isCollectionType() ) { - CollectionPersister collectionPersister = source.getFactory().getMetamodel().collectionPersister( ( (CollectionType) type ).getRole() ); + CollectionPersister collectionPersister = metamodel.collectionPersister( ( (CollectionType) type ).getRole() ); if ( collectionPersister.hasCache() ) { final CollectionDataAccess cache = collectionPersister.getCacheAccessStrategy(); final Object ck = cache.generateCacheKey( id, collectionPersister, - source.getFactory(), + factory, source.getTenantIdentifier() ); final SoftLock lock = cache.lockItem( source, ck, null ); cache.remove( source, ck ); - source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); + actionQueue.registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); } } else if ( type.isComponentType() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 4883160153..0589a070dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -422,7 +422,8 @@ public final class SessionImpl return super.shouldCloseJdbcCoordinatorOnClose( isTransactionCoordinatorShared ); } - if ( getActionQueue().hasBeforeTransactionActions() || getActionQueue().hasAfterTransactionActions() ) { + final ActionQueue actionQueue = getActionQueue(); + if ( actionQueue.hasBeforeTransactionActions() || actionQueue.hasAfterTransactionActions() ) { log.warn( "On close, shared Session had before/after transaction actions that have not yet been processed" ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/tm/AfterCompletionTest.java b/hibernate-core/src/test/java/org/hibernate/test/tm/AfterCompletionTest.java index ef0007f52e..b604577021 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/tm/AfterCompletionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/tm/AfterCompletionTest.java @@ -20,6 +20,7 @@ import org.hibernate.Session; import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.junit.Test; @@ -71,8 +72,9 @@ public class AfterCompletionTest extends BaseNonConfigCoreFunctionalTestCase { // The before causes the original thread to wait until Reaper aborts the transaction // The after tracks whether it is invoked since this test is to guarantee it is called final SessionImplementor sessionImplementor = (SessionImplementor) session; - sessionImplementor.getActionQueue().registerProcess( new AfterCallbackCompletionHandler() ); - sessionImplementor.getActionQueue().registerProcess( new BeforeCallbackCompletionHandler() ); + final ActionQueue actionQueue = sessionImplementor.getActionQueue(); + actionQueue.registerProcess( new AfterCallbackCompletionHandler() ); + actionQueue.registerProcess( new BeforeCallbackCompletionHandler() ); TestingJtaPlatformImpl.transactionManager().commit(); } From 25ca80b1c5a58862e94d0bc5679f2ca6c39617af Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sun, 11 Aug 2019 21:38:17 +0100 Subject: [PATCH 02/99] HHH-13563 ResultSetReturnImpl is looking up JdbcServices on each construction --- .../engine/jdbc/internal/JdbcCoordinatorImpl.java | 2 +- .../engine/jdbc/internal/ResultSetReturnImpl.java | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 9a4ba49a3b..0d5dd3876b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -223,7 +223,7 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { @Override public ResultSetReturn getResultSetReturn() { if ( resultSetExtractor == null ) { - resultSetExtractor = new ResultSetReturnImpl( this ); + resultSetExtractor = new ResultSetReturnImpl( this, jdbcServices ); } return resultSetExtractor; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java index 63298d5522..c39549eced 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java @@ -36,16 +36,9 @@ public class ResultSetReturnImpl implements ResultSetReturn { * * @param jdbcCoordinator The JdbcCoordinator */ - public ResultSetReturnImpl(JdbcCoordinator jdbcCoordinator) { + public ResultSetReturnImpl(JdbcCoordinator jdbcCoordinator, JdbcServices jdbcServices) { this.jdbcCoordinator = jdbcCoordinator; - - final JdbcServices jdbcServices = jdbcCoordinator.getJdbcSessionOwner() - .getJdbcSessionContext() - .getServiceRegistry() - .getService( JdbcServices.class ); - this.dialect = jdbcServices.getDialect(); - this.sqlStatementLogger = jdbcServices.getSqlStatementLogger(); this.sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); } From 3e17be98324a2aed8d18aab573c11a846e40c106 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sun, 11 Aug 2019 21:50:15 +0100 Subject: [PATCH 03/99] HHH-13562 List of TransactionObserver for JdbcResourceLocalTransactionCoordinatorImpl should be lazily initialized --- ...esourceLocalTransactionCoordinatorImpl.java | 18 ++++++++++++++---- .../SessionWithSharedConnectionTest.java | 4 +++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java index 1bbde93579..a637c01c19 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java @@ -7,6 +7,7 @@ package org.hibernate.resource.transaction.backend.jdbc.internal; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.persistence.RollbackException; import javax.transaction.Status; @@ -50,7 +51,7 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC private int timeOut = -1; - private final transient List observers; + private transient List observers = null; /** * Construct a ResourceLocalTransactionCoordinatorImpl instance. package-protected to ensure access goes through @@ -62,7 +63,6 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC TransactionCoordinatorBuilder transactionCoordinatorBuilder, TransactionCoordinatorOwner owner, JdbcResourceTransactionAccess jdbcResourceTransactionAccess) { - this.observers = new ArrayList<>(); this.transactionCoordinatorBuilder = transactionCoordinatorBuilder; this.jdbcResourceTransactionAccess = jdbcResourceTransactionAccess; this.transactionCoordinatorOwner = owner; @@ -81,7 +81,12 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC * @return TransactionObserver */ private Iterable observers() { - return new ArrayList<>( observers ); + if ( observers == null || observers.isEmpty() ) { + return Collections.emptyList(); + } + else { + return new ArrayList<>( observers ); + } } @Override @@ -203,12 +208,17 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC @Override public void addObserver(TransactionObserver observer) { + if ( observers == null ) { + observers = new ArrayList<>( 6 ); + } observers.add( observer ); } @Override public void removeObserver(TransactionObserver observer) { - observers.remove( observer ); + if ( observers != null ) { + observers.remove( observer ); + } } /** diff --git a/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java b/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java index 703174f6f8..ebec824f32 100644 --- a/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java @@ -270,7 +270,9 @@ public class SessionWithSharedConnectionTest extends BaseCoreFunctionalTestCase } assertNotNull("Observers field was not found", field); - assertEquals(0, ((Collection) field.get(((SessionImplementor) session).getTransactionCoordinator())).size()); + //Some of these collections could be lazily initialize: check for null before invoking size() + final Collection collection = (Collection) field.get( ( (SessionImplementor) session ).getTransactionCoordinator() ); + assertTrue(collection == null || collection.size() == 0 ); //open secondary sessions with managed options and immediately close Session secondarySession; From 1b06b76e17a41b977a43c48551c3a844704044e7 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 12 Aug 2019 19:38:09 -0700 Subject: [PATCH 04/99] HHH-13557 : LocalTimeTest#writeThenNativeRead and OffsetTimeTest#writeThenNativeRead tests are failing on SQL Server --- .../test/java/org/hibernate/test/type/LocalTimeTest.java | 7 ++++++- .../test/java/org/hibernate/test/type/OffsetTimeTest.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java index 9c80a3eba1..e4139ed48c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java @@ -175,7 +175,12 @@ public class LocalTimeTest extends AbstractJavaTimeTypeTest Date: Mon, 12 Aug 2019 21:30:20 -0700 Subject: [PATCH 05/99] HHH-13558 : InstantTest, LocalDateTimeTest, OffsetDateTimeTest, ZonedDateTimeTest failing on Sybase for year 1600 --- .../src/test/java/org/hibernate/test/type/InstantTest.java | 6 ++++++ .../java/org/hibernate/test/type/LocalDateTimeTest.java | 6 ++++++ .../java/org/hibernate/test/type/OffsetDateTimeTest.java | 6 ++++++ .../java/org/hibernate/test/type/ZonedDateTimeTest.java | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java index 2b960ae667..f1d159b8b0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java @@ -24,6 +24,7 @@ import javax.persistence.Id; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseDialect; import org.junit.runners.Parameterized; @@ -64,6 +65,11 @@ public class InstantTest extends AbstractJavaTimeTypeTest b .add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java index d61cd590c4..03e3cf0c5c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java @@ -21,6 +21,7 @@ import javax.persistence.Id; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseDialect; import org.junit.runners.Parameterized; @@ -59,6 +60,11 @@ public class LocalDateTimeTest extends AbstractJavaTimeTypeTest b .add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java index dab40cdc92..fa14923a5e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java @@ -25,6 +25,7 @@ import javax.persistence.Id; import org.hibernate.Query; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseDialect; import org.hibernate.type.OffsetDateTimeType; import org.hibernate.testing.TestForIssue; @@ -91,6 +92,11 @@ public class OffsetDateTimeTest extends AbstractJavaTimeTypeTest b .add( 1600, 1, 1, 0, 0, 0, 0, "+00:19:32", ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java index 61dad123e9..b7c7201575 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java @@ -25,6 +25,7 @@ import javax.persistence.Id; import org.hibernate.Query; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseDialect; import org.hibernate.type.ZonedDateTimeType; import org.hibernate.testing.TestForIssue; @@ -102,6 +103,11 @@ public class ZonedDateTimeTest extends AbstractJavaTimeTypeTest b .add( 1600, 1, 1, 0, 0, 0, 0, "GMT+00:19:32", ZONE_AMSTERDAM ) .add( 1600, 1, 1, 0, 0, 0, 0, "Europe/Amsterdam", ZONE_AMSTERDAM ) ) From 31fb14e0d9de1df1aa239e85542df54d992248bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 9 Aug 2019 19:02:03 +0200 Subject: [PATCH 06/99] HHH-13551 Restucture ClassPathAndModulePathAggregatedServiceLoader This does not change the behavior of the class at all: it simply restructures the code to allow for the changes in the next commits. --- .../internal/AggregatedServiceLoader.java | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java index eead6d561a..edc8907c56 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java @@ -145,17 +145,14 @@ abstract class AggregatedServiceLoader { * @param The type of loaded services. */ private static class ClassPathAndModulePathAggregatedServiceLoader extends AggregatedServiceLoader { + private final ServiceLoader aggregatedClassLoaderServiceLoader; private final List> delegates; private Collection cache = null; private ClassPathAndModulePathAggregatedServiceLoader(AggregatedClassLoader aggregatedClassLoader, Class serviceContract) { this.delegates = new ArrayList<>(); - // Always try the aggregated class loader first - this.delegates.add( ServiceLoader.load( serviceContract, aggregatedClassLoader ) ); - - // Then also try the individual class loaders, - // because only them can instantiate services provided by jars in the module path + this.aggregatedClassLoaderServiceLoader = ServiceLoader.load( serviceContract, aggregatedClassLoader ); final Iterator clIterator = aggregatedClassLoader.newClassLoaderIterator(); while ( clIterator.hasNext() ) { this.delegates.add( @@ -185,54 +182,67 @@ abstract class AggregatedServiceLoader { return cache; } - @SuppressWarnings("unchecked") private Collection loadAll() { Set alreadyEncountered = new HashSet<>(); Set result = new LinkedHashSet<>(); - delegates.stream() - // Each loader's stream() method returns a stream of service providers: flatten these into a single stream - .flatMap( delegate -> { - try { - return (Stream>) SERVICE_LOADER_STREAM_METHOD.invoke( delegate ); - } - catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { - throw new AssertionFailure( "Error calling ServiceLoader.stream()", e ); - } - } ) - // For each provider, check its type to be sure we don't use a provider twice, then get the service - .forEach( provider -> { - Class type; - try { - type = (Class) PROVIDER_TYPE_METHOD.invoke( provider ); - } - catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { - throw new AssertionFailure( "Error calling ServiceLoader.Provider.type()", e ); - } - String typeName = type.getName(); - /* - * We may encounter the same service provider multiple times, - * because the individual class loaders may give access to the same types - * (at the very least a single class loader may be present twice in the aggregated class loader). - * However, we only want to get the service from each provider once. - * - * ServiceLoader.stream() is useful in that regard, - * since it allows us to check the type of the service provider - * before the service is even instantiated. - * - * We could just instantiate every service and check their type afterwards, - * but 1. it would lead to unnecessary instantiation which could have side effects, - * in particular regarding class loading, - * and 2. the type of the provider may not always be the type of the service, - * and one provider may return different types of services - * depending on conditions known only to itself. - */ - if ( alreadyEncountered.add( typeName ) ) { - result.add( provider.get() ); - } - } ); + + // Always try the aggregated class loader first + providerStream( aggregatedClassLoaderServiceLoader ) + .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + + /* + * Then also try the individual class loaders, + * because only them can instantiate services provided by jars in the module path. + */ + for ( ServiceLoader delegate : delegates ) { + providerStream( delegate ) + .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + } + return result; } + @SuppressWarnings("unchecked") + private Stream> providerStream(ServiceLoader serviceLoader) { + try { + return ( (Stream>) SERVICE_LOADER_STREAM_METHOD.invoke( serviceLoader ) ); + } + catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { + throw new AssertionFailure( "Error calling ServiceLoader.stream()", e ); + } + } + + /* + * We may encounter the same service provider multiple times, + * because the individual class loaders may give access to the same types + * (at the very least a single class loader may be present twice in the aggregated class loader). + * However, we only want to get the service from each provider once. + * + * ServiceLoader.stream() is useful in that regard, + * since it allows us to check the type of the service provider + * before the service is even instantiated. + * + * We could just instantiate every service and check their type afterwards, + * but 1. it would lead to unnecessary instantiation which could have side effects, + * in particular regarding class loading, + * and 2. the type of the provider may not always be the type of the service, + * and one provider may return different types of services + * depending on conditions known only to itself. + */ + private void collectServiceIfNotDuplicate(Set result, Set alreadyEncountered, Supplier provider) { + Class type; + try { + type = (Class) PROVIDER_TYPE_METHOD.invoke( provider ); + } + catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { + throw new AssertionFailure( "Error calling ServiceLoader.Provider.type()", e ); + } + String typeName = type.getName(); + if ( alreadyEncountered.add( typeName ) ) { + result.add( provider.get() ); + } + } + @Override public void close() { cache = null; From 5174fc28dc2d4b13992af184179589f71083f4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 9 Aug 2019 19:15:04 +0200 Subject: [PATCH 07/99] HHH-13551 Ignore ServiceConfigurationError thrown when accessing services of individual (non-aggregated) class loaders --- .../internal/AggregatedServiceLoader.java | 42 +++++++++++++++++-- .../hibernate/internal/CoreMessageLogger.java | 5 +++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java index edc8907c56..6d902d046d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java @@ -15,12 +15,15 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Stream; import org.hibernate.AssertionFailure; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; /** * A service loader bound to an {@link AggregatedClassLoader}. @@ -28,6 +31,8 @@ import org.hibernate.AssertionFailure; */ abstract class AggregatedServiceLoader { + private static final CoreMessageLogger log = CoreLogging.messageLogger( AggregatedServiceLoader.class ); + private static final Method SERVICE_LOADER_STREAM_METHOD; private static final Method PROVIDER_TYPE_METHOD; @@ -145,12 +150,14 @@ abstract class AggregatedServiceLoader { * @param The type of loaded services. */ private static class ClassPathAndModulePathAggregatedServiceLoader extends AggregatedServiceLoader { + private final Class serviceContract; private final ServiceLoader aggregatedClassLoaderServiceLoader; private final List> delegates; private Collection cache = null; private ClassPathAndModulePathAggregatedServiceLoader(AggregatedClassLoader aggregatedClassLoader, Class serviceContract) { + this.serviceContract = serviceContract; this.delegates = new ArrayList<>(); this.aggregatedClassLoaderServiceLoader = ServiceLoader.load( serviceContract, aggregatedClassLoader ); final Iterator clIterator = aggregatedClassLoader.newClassLoaderIterator(); @@ -187,16 +194,32 @@ abstract class AggregatedServiceLoader { Set result = new LinkedHashSet<>(); // Always try the aggregated class loader first - providerStream( aggregatedClassLoaderServiceLoader ) - .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + Iterator> providerIterator = providerStream( aggregatedClassLoaderServiceLoader ) + .iterator(); + while ( providerIterator.hasNext() ) { + Supplier provider = providerIterator.next(); + collectServiceIfNotDuplicate( result, alreadyEncountered, provider ); + } /* * Then also try the individual class loaders, * because only them can instantiate services provided by jars in the module path. */ for ( ServiceLoader delegate : delegates ) { - providerStream( delegate ) - .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + providerIterator = providerStream( delegate ).iterator(); + /* + * Note that advancing the stream itself can lead to (arguably) "legitimate" errors, + * where we fail to load the service, + * but only because individual classloader has its own definition of the service contract class, + * which is different from ours. + * In that case (still arguably), the error should be ignored. + * That's why we wrap the call to hasNext in a method that catches an logs errors. + * See https://hibernate.atlassian.net/browse/HHH-13551. + */ + while ( hasNextIgnoringServiceConfigurationError( providerIterator ) ) { + Supplier provider = providerIterator.next(); + collectServiceIfNotDuplicate( result, alreadyEncountered, provider ); + } } return result; @@ -212,6 +235,17 @@ abstract class AggregatedServiceLoader { } } + private boolean hasNextIgnoringServiceConfigurationError(Iterator iterator) { + while ( true ) { + try { + return iterator.hasNext(); + } + catch (ServiceConfigurationError e) { + log.ignoringServiceConfigurationError( serviceContract, e ); + } + } + } + /* * We may encounter the same service provider multiple times, * because the individual class loaders may give access to the same types diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 99d4447451..55e392a429 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -15,6 +15,7 @@ import java.sql.SQLException; import java.sql.SQLWarning; import java.util.Hashtable; import java.util.Properties; +import java.util.ServiceConfigurationError; import java.util.Set; import javax.naming.NameNotFoundException; import javax.naming.NamingException; @@ -1871,4 +1872,8 @@ public interface CoreMessageLogger extends BasicLogger { @Message(value = "Multiple configuration properties defined to create schema. Choose at most one among 'javax.persistence.create-database-schemas', 'hibernate.hbm2ddl.create_namespaces', 'hibernate.hbm2dll.create_namespaces' (this last being deprecated).", id = 504) void multipleSchemaCreationSettingsDefined(); + @LogMessage(level = WARN) + @Message(value = "Ignoring ServiceConfigurationError caught while trying to instantiate service '%s'.", id = 505) + void ignoringServiceConfigurationError(Class serviceContract, @Cause ServiceConfigurationError error); + } From f2f788c03d2b5e95c46376d9300297439742b6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 9 Aug 2019 17:42:18 +0200 Subject: [PATCH 08/99] HHH-13551 Test the retrieval of a service when an "incompatible" classloader is provided --- .../internal/ClassLoaderServiceImplTest.java | 25 +++++++ .../internal/IsolatedClassLoader.java | 68 +++++++++++++++++++ .../classloading/internal/MyService.java | 12 ++++ .../classloading/internal/MyServiceImpl.java | 10 +++ ...t.registry.classloading.internal.MyService | 1 + 5 files changed, 116 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java create mode 100644 hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java index 78a7614108..14dfa58064 100644 --- a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java @@ -8,8 +8,11 @@ package org.hibernate.boot.registry.classloading.internal; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -124,6 +127,28 @@ public class ClassLoaderServiceImplTest { csi.stop(); } + @Test + @TestForIssue(jiraKey = "HHH-13551") + public void testServiceFromIncompatibleClassLoader() { + ClassLoaderServiceImpl classLoaderService = new ClassLoaderServiceImpl( + Arrays.asList( + getClass().getClassLoader(), + /* + * This classloader will return instances of MyService where MyService + * is a different object than the one we manipulate in the current classloader. + * This used to throw an exception that triggered a boot failure in ORM, + * but should now be ignored. + */ + new IsolatedClassLoader( getClass().getClassLoader() ) + ), + TcclLookupPrecedence.AFTER + ); + + Collection loadedServices = classLoaderService.loadJavaServices( MyService.class ); + + assertEquals( 1, loadedServices.size() ); + } + private static class InternalClassLoader extends ClassLoader { private List names = new ArrayList<>( ); diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java new file mode 100644 index 0000000000..a1a1a6d5c9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java @@ -0,0 +1,68 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.registry.classloading.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +import org.hibernate.bytecode.spi.ByteCodeHelper; + +/** + * A classloader isolated from the application classloader, + * simulating a separate classloader that can create duplicate classes. + * This can result in a Service implementation extending + * a Service interface that is essentially the same as the one manipulated by ORM, + * but is a different Class instance and is thus deemed different by the JVM. + */ +class IsolatedClassLoader extends ClassLoader { + /** + * Another classloader from which resources will be read. + * Classes available in that classloader will be duplicated in the isolated classloader. + */ + private final ClassLoader resourceSource; + + IsolatedClassLoader(ClassLoader resourceSource) { + super( getTopLevelClassLoader( resourceSource ) ); + this.resourceSource = resourceSource; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + InputStream is = getResourceAsStream( name.replace( '.', '/' ) + ".class" ); + if ( is == null ) { + throw new ClassNotFoundException( name + " not found" ); + } + + try { + byte[] bytecode = ByteCodeHelper.readByteCode( is ); + return defineClass( name, bytecode, 0, bytecode.length ); + } + catch( Throwable t ) { + throw new ClassNotFoundException( name + " not found", t ); + } + } + + @Override + public URL getResource(String name) { + return resourceSource.getResource( name ); + } + + @Override + public Enumeration getResources(String name) throws IOException { + return resourceSource.getResources( name ); + } + + private static ClassLoader getTopLevelClassLoader(ClassLoader classFileSource) { + ClassLoader result = classFileSource; + while ( result.getParent() != null ) { + result = result.getParent(); + } + return result; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java new file mode 100644 index 0000000000..ce0898803d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java @@ -0,0 +1,12 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.registry.classloading.internal; + +import org.hibernate.service.Service; + +public interface MyService extends Service { +} diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java new file mode 100644 index 0000000000..fa9f185022 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java @@ -0,0 +1,10 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.registry.classloading.internal; + +public class MyServiceImpl implements MyService { +} diff --git a/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService new file mode 100644 index 0000000000..89b4c390ee --- /dev/null +++ b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService @@ -0,0 +1 @@ +org.hibernate.boot.registry.classloading.internal.MyServiceImpl \ No newline at end of file From 2aee5a930d06ba10c79e08be5c37488ae5072a7c Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 13 Aug 2019 21:01:12 +0100 Subject: [PATCH 09/99] HHH-13556 Tests doing dynamic fetch scrolling a collection fail on DB2 --- .../lazy/StatelessQueryScrollingTest.java | 13 ++++++++++++- .../fetching/StatelessSessionFetchingTest.java | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/StatelessQueryScrollingTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/StatelessQueryScrollingTest.java index 1bc6da1809..2aa0d1cdf4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/StatelessQueryScrollingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/StatelessQueryScrollingTest.java @@ -24,6 +24,7 @@ import org.hibernate.StatelessSession; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.query.Query; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; @@ -73,7 +74,17 @@ public class StatelessQueryScrollingTest extends BaseNonConfigCoreFunctionalTest try { final Query query = statelessSession.createQuery( "select p from Producer p join fetch p.products" ); - scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY ); + if ( getDialect() instanceof DB2Dialect ) { + /* + FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst() + but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result + set type of TYPE_FORWARD_ONLY and db2 does not support it. + */ + scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE ); + } + else { + scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY ); + } while ( scrollableResults.next() ) { Producer producer = (Producer) scrollableResults.get( 0 ); assertTrue( Hibernate.isInitialized( producer ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/stateless/fetching/StatelessSessionFetchingTest.java b/hibernate-core/src/test/java/org/hibernate/test/stateless/fetching/StatelessSessionFetchingTest.java index 01f7d0e75f..1b058e74e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/stateless/fetching/StatelessSessionFetchingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/stateless/fetching/StatelessSessionFetchingTest.java @@ -18,6 +18,7 @@ import org.hibernate.StatelessSession; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl; import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.internal.util.StringHelper; @@ -224,7 +225,18 @@ public class StatelessSessionFetchingTest extends BaseCoreFunctionalTestCase { ss.beginTransaction(); final Query query = ss.createQuery( "select p from Producer p join fetch p.products" ); - final ScrollableResults scrollableResults = query.scroll(ScrollMode.FORWARD_ONLY); + ScrollableResults scrollableResults = null; + if ( getDialect() instanceof DB2Dialect ) { + /* + FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst() + but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result + set type of TYPE_FORWARD_ONLY and db2 does not support it. + */ + scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE ); + } + else { + scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY ); + } while ( scrollableResults.next() ) { Producer producer = (Producer) scrollableResults.get( 0 ); assertTrue( Hibernate.isInitialized( producer ) ); From 1a5b401d09e6ede20152e5d5855ab8fa1b9e9163 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 13 Aug 2019 16:08:36 +0100 Subject: [PATCH 10/99] HHH-13554 QueryAndSQLTest.testNativeQueryWithFormulaAttributeWithoutAlias() fails on MariaDB --- .../org/hibernate/test/annotations/query/QueryAndSQLTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java index eea98617b9..59089a6a31 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java @@ -103,7 +103,7 @@ public class QueryAndSQLTest extends BaseCoreFunctionalTestCase { sessionFactory() ); String sql = String.format( - "select TABLE_NAME , %s from all_tables where TABLE_NAME = 'AUDIT_ACTIONS' ", + "select TABLE_NAME , %s from ALL_TABLES where TABLE_NAME = 'AUDIT_ACTIONS' ", dateFunctionRendered ); Session s = openSession(); From 937d4a35031cd7ee4968bec26b09de5815c4395e Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 13 Aug 2019 18:02:32 -0700 Subject: [PATCH 11/99] HHH-13569 : org.hibernate.test.annotations.embedded.EmbeddedTest failures on Sybase --- .../test/annotations/embedded/EmbeddedTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java index 83cdea6f8e..7b44272661 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java @@ -18,8 +18,10 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; +import org.hibernate.dialect.SybaseDialect; import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.testing.transaction.TransactionUtil; @@ -27,6 +29,7 @@ import org.hibernate.test.annotations.embedded.FloatLeg.RateIndex; import org.hibernate.test.annotations.embedded.Leg.Frequency; import org.hibernate.test.util.SchemaUtil; +import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -39,6 +42,16 @@ import static org.junit.Assert.assertTrue; * @author Emmanuel Bernard */ public class EmbeddedTest extends BaseNonConfigCoreFunctionalTestCase { + + @After + public void cleanup() { + TransactionUtil.doInHibernate( this::sessionFactory, session ->{ + for ( Person person : session.createQuery( "from Person", Person.class ).getResultList() ) { + session.delete( person ); + } + } ); + } + @Test public void testSimple() throws Exception { Person person = new Person(); @@ -145,6 +158,7 @@ public class EmbeddedTest extends BaseNonConfigCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-8172") + @SkipForDialect( value = SybaseDialect.class, comment = "skip for Sybase because (null = null) evaluates to true") @FailureExpected(jiraKey = "HHH-8172") public void testQueryWithEmbeddedParameterOneNull() throws Exception { Person person = new Person(); From 9988d677b955154d188671cc0086153444b3e70c Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 13 Aug 2019 23:16:45 -0700 Subject: [PATCH 12/99] HHH-13571 : Test failures due to cross joined table out of scope of a subsequent JOIN on Sybase --- .../src/test/java/org/hibernate/test/hql/EntityJoinTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/EntityJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/EntityJoinTest.java index e140b5b091..7efff1e585 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/EntityJoinTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/EntityJoinTest.java @@ -16,9 +16,11 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.annotations.NaturalId; +import org.hibernate.dialect.SybaseDialect; import org.hibernate.engine.query.spi.HQLQueryPlan; import org.hibernate.hql.spi.QueryTranslator; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.After; @@ -105,6 +107,7 @@ public class EntityJoinTest extends BaseNonConfigCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-11337") + @SkipForDialect(SybaseDialect.class) public void testLeftOuterEntityJoinsWithImplicitInnerJoinInSelectClause() { doInHibernate( this::sessionFactory, session -> { // this should get all financial records even if their lastUpdateBy user is null From ce03ef96b9e46a673f7f6f345e8eaecb8ff3f309 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 13 Aug 2019 23:37:47 -0700 Subject: [PATCH 13/99] HHH-13570 : Test failures due to Sybase not supporting UPDATE statement with WITH(NOWAIT) --- .../test/java/org/hibernate/test/locking/LockModeTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java b/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java index 6dd9305a2d..707f52125e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java @@ -19,6 +19,8 @@ import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -60,6 +62,7 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { protected boolean isCleanupTestDataRequired(){return true;} @Test + @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class ) @SuppressWarnings( {"deprecation"}) public void testLoading() { // open a session, begin a transaction and lock row @@ -75,6 +78,7 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { } @Test + @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class ) public void testLegacyCriteria() { // open a session, begin a transaction and lock row doInHibernate( this::sessionFactory, session -> { @@ -92,6 +96,7 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { } @Test + @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class ) public void testLegacyCriteriaAliasSpecific() { // open a session, begin a transaction and lock row doInHibernate( this::sessionFactory, session -> { @@ -108,6 +113,7 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { } @Test + @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class ) public void testQuery() { // open a session, begin a transaction and lock row doInHibernate( this::sessionFactory, session -> { From fed93b0ae96bd39367897cbbec66327ddd83962d Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 14 Aug 2019 00:25:49 -0700 Subject: [PATCH 14/99] HHH-13573 : Test failure due to Sybase not supporting cascade delete on foreign key definitions --- .../hibernate/test/annotations/onetomany/OneToManyTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OneToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OneToManyTest.java index df06757ac2..0027981c70 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OneToManyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OneToManyTest.java @@ -37,6 +37,8 @@ import org.hibernate.mapping.Column; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Table; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.test.annotations.Customer; @@ -359,6 +361,7 @@ public class OneToManyTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @RequiresDialectFeature(DialectChecks.SupportsCascadeDeleteCheck.class) public void testCascadeDeleteWithUnidirectionalAssociation() throws Exception { OnDeleteUnidirectionalOneToManyChild child = new OnDeleteUnidirectionalOneToManyChild(); From 35037dac7b6e080c7d1dae31a64d0d5012b1b40c Mon Sep 17 00:00:00 2001 From: Legohuman Date: Sat, 10 Aug 2019 00:33:24 +0200 Subject: [PATCH 15/99] HHH-13259 Fix StackOverflowError in StringHelper Before fix method org.hibernate.internal.util.StringHelper#replace matched placeholders illegally in case when ordinal parameters list was expanded. Ex. placeholder ?1 was matched with ?11, ?12, ?13 etc. For queries with 2 or more IN clauses with different collections there were a situation when ?1 from the first clause matched with already expanded placeholders from the second collection. Each match led to recursive call of replace method. If collection in second clause was very long then StackOverflowError occurred. Fix adds check of partial placeholder match for wholeWords mode which is used in expanding list parameters. Partial matches are skipped during replace. --- .../hibernate/internal/util/StringHelper.java | 20 ++++++++++++++++++- .../hibernate/test/util/StringHelperTest.java | 20 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 9c658ec37f..f79b03a651 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -123,7 +123,7 @@ public final class StringHelper { if ( template == null ) { return null; } - int loc = template.indexOf( placeholder ); + int loc = indexOfPlaceHolder( template, placeholder, wholeWords ); if ( loc < 0 ) { return template; } @@ -189,6 +189,24 @@ public final class StringHelper { return buf.toString(); } + private static int indexOfPlaceHolder(String template, String placeholder, boolean wholeWords) { + if ( wholeWords ) { + int placeholderIndex = -1; + boolean isPartialPlaceholderMatch; + do { + placeholderIndex = template.indexOf( placeholder, placeholderIndex + 1 ); + isPartialPlaceholderMatch = placeholderIndex != -1 && + template.length() > placeholderIndex + placeholder.length() && + Character.isJavaIdentifierPart( template.charAt( placeholderIndex + placeholder.length() ) ); + } while ( placeholderIndex != -1 && isPartialPlaceholderMatch ); + + return placeholderIndex; + } + else { + return template.indexOf( placeholder ); + } + } + /** * Used to find the ordinal parameters (e.g. '?1') in a string. */ diff --git a/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java b/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java index 299b60bc32..545a638753 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java @@ -100,4 +100,24 @@ public class StringHelperTest extends BaseUnitTestCase { Assert.assertFalse( StringHelper.isQuoted( "a", sqlServerDialect ) ); } + @Test + public void replaceRepeatingPlaceholdersWithoutStackOverflow() { + String ordinalParameters = generateOrdinalParameters( 3, 19999 ); + String result = StringHelper.replace( + "select * from books where category in (?1) and id in(" + ordinalParameters + ") and parent_category in (?1) and id in(" + ordinalParameters + ")", + "?1", "?1, ?2", true, true ); + assertEquals( "select * from books where category in (?1, ?2) and id in(" + ordinalParameters + ") and parent_category in (?1, ?2) and id in(" + ordinalParameters + ")", result ); + } + + private String generateOrdinalParameters(int startPosition, int endPosition) { + StringBuilder builder = new StringBuilder(); + for ( int i = startPosition; i <= endPosition; i++ ) { + builder.append( '?' ).append( i ); + if ( i < endPosition ) { + builder.append( ", " ); + } + } + return builder.toString(); + } + } From 77462271156d8eeb595a92aaaebdf6b919f745cc Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 14 Aug 2019 11:04:47 +0100 Subject: [PATCH 16/99] HHH-13576 Invoking tracef() or debugf() w/o an array of parameters actually allocates an empty Object[] --- .../src/main/java/org/hibernate/internal/SessionImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 0589a070dc..324dcdee91 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -1109,7 +1109,7 @@ public final class SessionImpl boolean clearedEffectiveGraph = false; if ( semantic != null ) { if ( ! graph.appliesTo( entityName ) ) { - log.debugf( "Clearing effective entity graph for subsequent-select" ); + log.debug( "Clearing effective entity graph for subsequent-select" ); clearedEffectiveGraph = true; effectiveEntityGraph.clear(); } @@ -2512,7 +2512,7 @@ public final class SessionImpl @Override public void beforeTransactionCompletion() { - log.tracef( "SessionImpl#beforeTransactionCompletion()" ); + log.trace( "SessionImpl#beforeTransactionCompletion()" ); flushBeforeTransactionCompletion(); actionQueue.beforeTransactionCompletion(); try { From e110ab00300107e46af52b98eb1874681d833e0c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 14 Aug 2019 11:07:46 +0100 Subject: [PATCH 17/99] HHH-13576 A couple more Logger mistakes in SessionImpl --- .../org/hibernate/internal/SessionImpl.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 324dcdee91..f7b17e2bcb 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -958,12 +958,14 @@ public final class SessionImpl } private void logRemoveOrphanBeforeUpdates(String timing, String entityName, Object entity) { - final EntityEntry entityEntry = persistenceContext.getEntry( entity ); - log.tracef( - "%s remove orphan before updates: [%s]", - timing, - entityEntry == null ? entityName : MessageHelper.infoString( entityName, entityEntry.getId() ) - ); + if ( log.isTraceEnabled() ) { + final EntityEntry entityEntry = persistenceContext.getEntry( entity ); + log.tracef( + "%s remove orphan before updates: [%s]", + timing, + entityEntry == null ? entityName : MessageHelper.infoString( entityName, entityEntry.getId() ) + ); + } } private void fireDelete(DeleteEvent event) { @@ -2526,7 +2528,9 @@ public final class SessionImpl @Override public void afterTransactionCompletion(boolean successful, boolean delayed) { - log.tracef( "SessionImpl#afterTransactionCompletion(successful=%s, delayed=%s)", successful, delayed ); + if ( log.isTraceEnabled() ) { + log.tracef( "SessionImpl#afterTransactionCompletion(successful=%s, delayed=%s)", successful, delayed ); + } if ( !isClosed() || waitingForAutoClose ) { if ( autoClear ||!successful ) { From 6b489474f58033fe9b52e1c5c866b421c8c4f3e2 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 14 Aug 2019 11:31:32 +0100 Subject: [PATCH 18/99] HHH-13576 Similar debugf and tracef issues found via grepping --- .../boot/spi/XmlMappingBinderAccess.java | 4 ++-- .../main/java/org/hibernate/cfg/Settings.java | 2 +- .../internal/JtaPlatformInitiator.java | 4 ++-- .../internal/DefaultLoadEventListener.java | 2 +- .../EntityCopyObserverFactoryInitiator.java | 8 ++++---- .../org/hibernate/internal/IteratorImpl.java | 4 ++-- .../internal/SessionFactoryRegistry.java | 2 +- .../antlr/OrderByFragmentTranslator.java | 2 +- .../HibernatePersistenceProviderAdaptor.java | 18 ++++++++++-------- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java index f2d7faf89e..8102865f2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java @@ -83,13 +83,13 @@ public class XmlMappingBinderAccess { xmlInputStream.close(); } catch (IOException e) { - LOG.debugf( "Unable to close InputStream obtained from InputStreamAccess : " + xmlInputStreamAccess.getStreamName() ); + LOG.debugf( "Unable to close InputStream obtained from InputStreamAccess : %s", xmlInputStreamAccess.getStreamName() ); } } } public Binding bind(InputStream xmlInputStream) { - LOG.tracef( "reading mappings from InputStream" ); + LOG.trace( "reading mappings from InputStream" ); final Origin origin = new Origin( SourceType.INPUT_STREAM, null ); return new InputStreamXmlSource( origin, xmlInputStream, false ).doBind( getMappingBinder() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java index 046fa59c46..c2d590d984 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java @@ -76,7 +76,7 @@ public final class Settings { LOG.debugf( "Check Nullability in Core (should be disabled when Bean Validation is on): %s", enabledDisabled( sessionFactoryOptions.isCheckNullability() ) ); LOG.debugf( "Allow initialization of lazy state outside session : %s", enabledDisabled( sessionFactoryOptions.isInitializeLazyStateOutsideTransactionsEnabled() ) ); - LOG.debugf( "Using BatchFetchStyle : " + sessionFactoryOptions.getBatchFetchStyle().name() ); + LOG.debugf( "Using BatchFetchStyle : %", sessionFactoryOptions.getBatchFetchStyle().name() ); LOG.debugf( "Default batch fetch size: %s", sessionFactoryOptions.getDefaultBatchFetchSize() ); LOG.debugf( "Maximum outer join fetch depth: %s", sessionFactoryOptions.getMaximumFetchDepth() ); LOG.debugf( "Default null ordering: %s", sessionFactoryOptions.getDefaultNullPrecedence() ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformInitiator.java index c98d5a7fb7..ee9b808779 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformInitiator.java @@ -40,12 +40,12 @@ public class JtaPlatformInitiator implements StandardServiceInitiator Date: Wed, 14 Aug 2019 09:29:20 +0100 Subject: [PATCH 19/99] HHH-13574 SybaseASE does not support PARTITION BY --- .../main/java/org/hibernate/dialect/SybaseASE15Dialect.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java index 4d75e1bdc3..0027263e19 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java @@ -431,4 +431,9 @@ public class SybaseASE15Dialect extends SybaseDialect { public boolean supportsLockTimeouts() { return false; } + + @Override + public boolean supportsPartitionBy() { + return false; + } } From 210aff098c2b1d1c59e7780829d9c19bfff08e46 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 14 Aug 2019 11:55:11 +0100 Subject: [PATCH 20/99] HHH-13577 LockTest.testContendedPessimisticLock and StatementIsClosedAfterALockExceptionTest.testStatementIsClosed tests fail on Sybase HHH-13577 : Re-enable LockTest for SybaseASE15Dialect --- .../java/org/hibernate/jpa/test/lock/LockTest.java | 1 - .../testing/transaction/TransactionUtil.java | 7 +++++++ .../org/hibernate/testing/util/ExceptionUtil.java | 12 +++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java index dc6e41c282..09c149d46c 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java @@ -559,7 +559,6 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { @SkipForDialect(HSQLDialect.class) // ASE15.5 will generate select...holdlock and fail at this test, but ASE15.7 passes it. Skip it for ASE15.5 // only. - @SkipForDialect(value = { SybaseASE15Dialect.class }, strictMatching = true, jiraKey = "HHH-6820") @SkipForDialect(value = { SQLServerDialect.class }) public void testContendedPessimisticLock() throws Exception { final CountDownLatch latch = new CountDownLatch( 1 ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java index 95e2ce9e5b..67de5eb55c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java @@ -32,6 +32,7 @@ import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.jboss.logging.Logger; @@ -573,6 +574,12 @@ public class TransactionUtil { st.execute(String.format( "SET TRANSACTION LOCK WAIT TIMEOUT %d", millis )); } } + else if( Dialect.getDialect() instanceof SybaseASE15Dialect) { + try (Statement st = connection.createStatement()) { + //Prepared Statements fail for SET commands + st.execute(String.format( "SET LOCK WAIT %d", millis/1000 )); + } + } else { try { connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), (int) millis ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java index cb244aecea..c2ef8bef79 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java @@ -79,11 +79,13 @@ public class ExceptionUtil { else { Throwable rootCause = ExceptionUtil.rootCause( e ); if ( - rootCause != null && ( - rootCause.getMessage().contains( "timeout" ) || - rootCause.getMessage().contains( "timed out" ) || - rootCause.getMessage().contains( "lock(s) could not be acquired" ) - ) + rootCause != null && ( + rootCause.getMessage().contains( "timeout" ) || + rootCause.getMessage().contains( "timed out" ) || + rootCause.getMessage().contains( "lock(s) could not be acquired" ) || + rootCause.getMessage().contains( "Could not acquire a lock" ) + + ) ) { return true; } From 7c57047f4a039221bad4dbaddae0dee03c8848be Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 15 Aug 2019 01:28:23 -0700 Subject: [PATCH 21/99] HHH-13569 : Shorten table name that's too long for Oracle --- .../org/hibernate/test/annotations/embedded/EmbeddedTest.java | 2 +- .../org/hibernate/test/annotations/embedded/WealthyPerson.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java index 7b44272661..526a7e062d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java @@ -615,7 +615,7 @@ public class EmbeddedTest extends BaseNonConfigCoreFunctionalTestCase { public void testDefaultCollectionTable() throws Exception { //are the tables correct? assertTrue( SchemaUtil.isTablePresent( "WealthyPerson_vacationHomes", metadata() ) ); - assertTrue( SchemaUtil.isTablePresent( "WealthyPerson_legacyVacationHomes", metadata() ) ); + assertTrue( SchemaUtil.isTablePresent( "WelPers_LegacyVacHomes", metadata() ) ); assertTrue( SchemaUtil.isTablePresent( "WelPers_VacHomes", metadata() ) ); //just to make sure, use the mapping diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/WealthyPerson.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/WealthyPerson.java index 3b24b37908..05df8f1df6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/WealthyPerson.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/WealthyPerson.java @@ -19,6 +19,7 @@ public class WealthyPerson extends Person { protected Set
vacationHomes = new HashSet
(); @ElementCollection + @CollectionTable(name = "WelPers_LegacyVacHomes") protected Set
legacyVacationHomes = new HashSet
(); @ElementCollection From 927f4c2ffc4ae5c45d2ede9d8302d52e7014085e Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 16 Aug 2019 11:57:37 +0100 Subject: [PATCH 22/99] HHH-13584 Reduce ServiceRegistry lookups in LocalConnectionAccess in SessionFactory --- .../internal/SessionFactoryImpl.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 1f380c8cd4..d311b4d18a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -68,6 +68,7 @@ import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jndi.spi.JndiService; import org.hibernate.engine.profile.Association; @@ -458,29 +459,14 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { } private JdbcConnectionAccess buildLocalConnectionAccess() { - return new JdbcConnectionAccess() { - @Override - public Connection obtainConnection() throws SQLException { - return !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() - ? serviceRegistry.getService( ConnectionProvider.class ).getConnection() - : serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection(); - } - - @Override - public void releaseConnection(Connection connection) throws SQLException { - if ( !settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) { - serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection ); - } - else { - serviceRegistry.getService( MultiTenantConnectionProvider.class ).releaseAnyConnection( connection ); - } - } - - @Override - public boolean supportsAggressiveRelease() { - return false; - } - }; + if ( settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) { + final MultiTenantConnectionProvider mTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class ); + return new JdbcEnvironmentInitiator.MultiTenantConnectionProviderJdbcConnectionAccess( mTenantConnectionProvider ); + } + else { + final ConnectionProvider connectionProvider = serviceRegistry.getService( ConnectionProvider.class ); + return new JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess( connectionProvider ); + } } public Session openSession() throws HibernateException { From 0b64cef2b3d047fba460f984514446cc46a29db9 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 14 Aug 2019 13:36:37 +0100 Subject: [PATCH 23/99] HHH-13579 Optimise ResourceRegistryStandardImpl to avoid heavy allocation of iterators --- .../ResourceRegistryStandardImpl.java | 103 ++++++++++-------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java index e78f1590e3..a40d738eda 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java @@ -13,13 +13,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.JDBCException; @@ -29,19 +23,40 @@ import org.hibernate.resource.jdbc.ResourceRegistry; import org.hibernate.resource.jdbc.spi.JdbcObserver; /** + * Helps to track statements and resultsets which need being closed. + * This class is not threadsafe. + * + * Note regarding performance: we had evidence that allocating Iterators + * to implement the cleanup on each element recursively was the dominant + * resource cost, so we decided using "forEach" and lambdas in this case. + * However the forEach/lambda combination is able to dodge allocating + * Iterators on HashMap and ArrayList, but not on HashSet (at least on JDK8 and 11). + * Therefore some types which should ideally be modelled as a Set have + * been implemented using HashMap. + * * @author Steve Ebersole + * @author Sanne Grinovero */ -public class ResourceRegistryStandardImpl implements ResourceRegistry { +public final class ResourceRegistryStandardImpl implements ResourceRegistry { + private static final CoreMessageLogger log = CoreLogging.messageLogger( ResourceRegistryStandardImpl.class ); + // Dummy value to associate with an Object in the backing Map when we use it as a set: + private static final Object PRESENT = new Object(); + + //Used instead of Collections.EMPTY_SET to avoid polymorhic calls on xref; + //Also, uses an HashMap as it were an HashSet, as technically we just need the Set semantics + //but in this case the overhead of HashSet is not negligible. + private static final HashMap EMPTY = new HashMap( 1, 0.2f ); + private final JdbcObserver jdbcObserver; - private final Map> xref = new HashMap>(); - private final Set unassociatedResultSets = new HashSet(); + private final HashMap> xref = new HashMap<>(); + private final HashMap unassociatedResultSets = new HashMap(); - private List blobs; - private List clobs; - private List nclobs; + private ArrayList blobs; + private ArrayList clobs; + private ArrayList nclobs; private Statement lastQuery; @@ -67,7 +82,7 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { public void register(Statement statement, boolean cancelable) { log.tracef( "Registering statement [%s]", statement ); - Set previousValue = xref.putIfAbsent( statement, Collections.EMPTY_SET ); + HashMap previousValue = xref.putIfAbsent( statement, EMPTY ); if ( previousValue != null ) { throw new HibernateException( "JDBC Statement already registered" ); } @@ -81,7 +96,7 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { public void release(Statement statement) { log.tracev( "Releasing statement [{0}]", statement ); - final Set resultSets = xref.remove( statement ); + final HashMap resultSets = xref.remove( statement ); if ( resultSets != null ) { closeAll( resultSets ); } @@ -111,7 +126,7 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { } } if ( statement != null ) { - final Set resultSets = xref.get( statement ); + final HashMap resultSets = xref.get( statement ); if ( resultSets == null ) { log.unregisteredStatement(); } @@ -123,19 +138,16 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { } } else { - final boolean removed = unassociatedResultSets.remove( resultSet ); - if ( !removed ) { + final Object removed = unassociatedResultSets.remove( resultSet ); + if ( removed == null ) { log.unregisteredResultSetWithoutStatement(); } - } close( resultSet ); } - protected void closeAll(Set resultSets) { - for ( ResultSet resultSet : resultSets ) { - close( resultSet ); - } + protected void closeAll(final HashMap resultSets) { + resultSets.forEach( (resultSet, o) -> close( resultSet ) ); resultSets.clear(); } @@ -202,7 +214,7 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { } } if ( statement != null ) { - Set resultSets = xref.get( statement ); + HashMap resultSets = xref.get( statement ); // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. @@ -210,14 +222,14 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { log.debug( "ResultSet statement was not registered (on register)" ); } - if ( resultSets == null || resultSets == Collections.EMPTY_SET ) { - resultSets = new HashSet(); + if ( resultSets == null || resultSets == EMPTY ) { + resultSets = new HashMap(); xref.put( statement, resultSets ); } - resultSets.add( resultSet ); + resultSets.put( resultSet, PRESENT ); } else { - unassociatedResultSets.add( resultSet ); + unassociatedResultSets.put( resultSet, PRESENT ); } } @@ -303,62 +315,63 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry { jdbcObserver.jdbcReleaseRegistryResourcesStart(); } - for ( Map.Entry> entry : xref.entrySet() ) { - if ( entry.getValue() != null ) { - closeAll( entry.getValue() ); - } - close( entry.getKey() ); - } + xref.forEach( + (Statement s, HashMap r) -> { + closeAll( r ); + close( s ); + } + ); xref.clear(); closeAll( unassociatedResultSets ); if ( blobs != null ) { - for ( Blob blob : blobs ) { + blobs.forEach( blob -> { try { blob.free(); } catch (SQLException e) { log.debugf( "Unable to free JDBC Blob reference [%s]", e.getMessage() ); } - } - blobs.clear(); + } ); + //for these, it seems better to null the map rather than clear it: + blobs = null; } if ( clobs != null ) { - for ( Clob clob : clobs ) { + clobs.forEach( clob -> { try { clob.free(); } catch (SQLException e) { log.debugf( "Unable to free JDBC Clob reference [%s]", e.getMessage() ); } - } - clobs.clear(); + } ); + clobs = null; } if ( nclobs != null ) { - for ( NClob nclob : nclobs ) { + nclobs.forEach( nclob -> { try { nclob.free(); } catch (SQLException e) { log.debugf( "Unable to free JDBC NClob reference [%s]", e.getMessage() ); } - } - nclobs.clear(); + } ); + nclobs = null; } - if ( jdbcObserver != null ) { + if ( jdbcObserver != null ) { jdbcObserver.jdbcReleaseRegistryResourcesEnd(); } } - private boolean hasRegistered(Map resource) { + private boolean hasRegistered(final HashMap resource) { return resource != null && !resource.isEmpty(); } - private boolean hasRegistered(Collection resource) { + private boolean hasRegistered(final ArrayList resource) { return resource != null && !resource.isEmpty(); } } From 0a1213be1aa2b8bda4cdc3219dab573cd5b9adad Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 16 Aug 2019 14:59:37 +0100 Subject: [PATCH 24/99] HHH-13585 Duplicate resource release in PessimisticReadSelectLockingStrategy --- .../PessimisticReadSelectLockingStrategy.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java index 0b1b6d3d2f..319967ba82 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java @@ -73,17 +73,12 @@ public class PessimisticReadSelectLockingStrategy extends AbstractSelectLockingS } final ResultSet rs = jdbcCoordinator.getResultSetReturn().extract( st ); - try { - if ( !rs.next() ) { - final StatisticsImplementor statistics = factory.getStatistics(); - if ( statistics.isStatisticsEnabled() ) { - statistics.optimisticFailure( lockable.getEntityName() ); - } - throw new StaleObjectStateException( lockable.getEntityName(), id ); + if ( !rs.next() ) { + final StatisticsImplementor statistics = factory.getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.optimisticFailure( lockable.getEntityName() ); } - } - finally { - jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( rs, st ); + throw new StaleObjectStateException( lockable.getEntityName(), id ); } } finally { From 0a3f62abbffacb0fcd8d611942fce46f5dea5761 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 17 Aug 2019 19:38:30 +0300 Subject: [PATCH 25/99] HHH-13588 Add missed functions to MySQL Dialect: weight_string, to_base64, from_base64, regexp_replace, regexp_instr, regexp_substr --- .../main/java/org/hibernate/dialect/MySQL57Dialect.java | 7 +++++++ .../src/main/java/org/hibernate/dialect/MySQL8Dialect.java | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL57Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL57Dialect.java index 2adf0514bc..8f9ac9742a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL57Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL57Dialect.java @@ -9,7 +9,9 @@ package org.hibernate.dialect; import java.sql.Types; import org.hibernate.dialect.function.SQLFunction; +import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.StaticPrecisionFspTimestampFunction; +import org.hibernate.type.StandardBasicTypes; /** * @author Gail Badner @@ -64,6 +66,11 @@ public class MySQL57Dialect extends MySQL55Dialect { // from_unixtime(), timestamp() are functions that return TIMESTAMP that do not support a // fractional seconds precision argument (so there's no need to override them here): + + registerFunction( "weight_string", new StandardSQLFunction( "weight_string", StandardBasicTypes.STRING ) ); + + registerFunction( "to_base64", new StandardSQLFunction( "to_base64", StandardBasicTypes.STRING ) ); + registerFunction( "from_base64", new StandardSQLFunction( "from_base64", StandardBasicTypes.STRING ) ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL8Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL8Dialect.java index 4d145c11fc..dcda2359b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQL8Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQL8Dialect.java @@ -7,6 +7,8 @@ package org.hibernate.dialect; import org.hibernate.LockOptions; +import org.hibernate.dialect.function.StandardSQLFunction; +import org.hibernate.type.StandardBasicTypes; /** * @author Vlad Mihalcea @@ -33,6 +35,10 @@ public class MySQL8Dialect extends MySQL57Dialect { registerKeyword("PERSIST_ONLY"); registerKeyword("RANK"); registerKeyword("ROW_NUMBER"); + + registerFunction( "regexp_replace", new StandardSQLFunction( "regexp_replace", StandardBasicTypes.STRING ) ); + registerFunction( "regexp_instr", new StandardSQLFunction( "regexp_instr", StandardBasicTypes.INTEGER ) ); + registerFunction( "regexp_substr", new StandardSQLFunction( "regexp_substr", StandardBasicTypes.STRING ) ); } @Override From f49c97c0aa6777677dc5adc1eb57586acff06f80 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sun, 18 Aug 2019 14:46:37 +0100 Subject: [PATCH 26/99] HHH-13589 ActionQueue review: code formatting --- .../org/hibernate/engine/spi/ActionQueue.java | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index 909a7efc8e..1e7ed134c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -231,11 +231,11 @@ public class ActionQueue { public void clear() { for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) { ExecutableList l = listProvider.get( this ); - if( l != null ) { + if ( l != null ) { l.clear(); } } - if( unresolvedInsertions != null ) { + if ( unresolvedInsertions != null ) { unresolvedInsertions.clear(); } } @@ -267,7 +267,7 @@ public class ActionQueue { LOG.tracev( "Adding insert with non-nullable, transient entities; insert=[{0}], dependencies=[{1}]", insert, nonNullableTransientDependencies.toLoggableString( insert.getSession() ) ); } - if( unresolvedInsertions == null ) { + if ( unresolvedInsertions == null ) { unresolvedInsertions = new UnresolvedEntityInsertActions(); } unresolvedInsertions.addUnresolvedEntityInsertAction( insert, nonNullableTransientDependencies ); @@ -288,7 +288,7 @@ public class ActionQueue { if ( !insert.isVeto() ) { insert.makeEntityManaged(); - if( unresolvedInsertions != null ) { + if ( unresolvedInsertions != null ) { for ( AbstractEntityInsertAction resolvedAction : unresolvedInsertions.resolveDependentActions( insert.getInstance(), session ) ) { addResolvedEntityInsertAction( resolvedAction ); } @@ -390,8 +390,8 @@ public class ActionQueue { } private void registerCleanupActions(Executable executable) { - if( executable.getBeforeTransactionCompletionProcess() != null ) { - if( beforeTransactionProcesses == null ) { + if ( executable.getBeforeTransactionCompletionProcess() != null ) { + if ( beforeTransactionProcesses == null ) { beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); } beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() ); @@ -399,8 +399,8 @@ public class ActionQueue { if ( session.getFactory().getSessionFactoryOptions().isQueryCacheEnabled() ) { invalidateSpaces( convertTimestampSpaces( executable.getPropertySpaces() ) ); } - if( executable.getAfterTransactionCompletionProcess() != null ) { - if( afterTransactionProcesses == null ) { + if ( executable.getAfterTransactionCompletionProcess() != null ) { + if ( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() ); @@ -432,20 +432,20 @@ public class ActionQueue { * the first unresolved entity insert action. */ public void checkNoUnresolvedActionsAfterOperation() throws PropertyValueException { - if(unresolvedInsertions != null) { + if ( unresolvedInsertions != null ) { unresolvedInsertions.checkNoUnresolvedActionsAfterOperation(); } } public void registerProcess(AfterTransactionCompletionProcess process) { - if( afterTransactionProcesses == null ) { + if ( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } afterTransactionProcesses.register( process ); } public void registerProcess(BeforeTransactionCompletionProcess process) { - if( beforeTransactionProcesses == null ) { + if ( beforeTransactionProcesses == null ) { beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); } beforeTransactionProcesses.register( process ); @@ -493,7 +493,7 @@ public class ActionQueue { } private void prepareActions(ExecutableList queue) throws HibernateException { - if( queue == null ) { + if ( queue == null ) { return; } for ( Executable executable : queue ) { @@ -509,7 +509,7 @@ public class ActionQueue { public void afterTransactionCompletion(boolean success) { if ( !isTransactionCoordinatorShared ) { // Execute completion actions only in transaction owner (aka parent session). - if( afterTransactionProcesses != null ) { + if ( afterTransactionProcesses != null ) { afterTransactionProcesses.afterTransactionCompletion( success ); } } @@ -521,7 +521,7 @@ public class ActionQueue { public void beforeTransactionCompletion() { if ( !isTransactionCoordinatorShared ) { // Execute completion actions only in transaction owner (aka parent session). - if( beforeTransactionProcesses != null ) { + if ( beforeTransactionProcesses != null ) { beforeTransactionProcesses.beforeTransactionCompletion(); } } @@ -553,7 +553,7 @@ public class ActionQueue { return true; } } - if(unresolvedInsertions == null) { + if ( unresolvedInsertions == null ) { return false; } return areTablesToBeUpdated( unresolvedInsertions, tables ); @@ -604,14 +604,14 @@ public class ActionQueue { e.execute(); } finally { - if( e.getBeforeTransactionCompletionProcess() != null ) { - if( beforeTransactionProcesses == null ) { + if ( e.getBeforeTransactionCompletionProcess() != null ) { + if ( beforeTransactionProcesses == null ) { beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); } beforeTransactionProcesses.register( e.getBeforeTransactionCompletionProcess() ); } - if( e.getAfterTransactionCompletionProcess() != null ) { - if( afterTransactionProcesses == null ) { + if ( e.getAfterTransactionCompletionProcess() != null ) { + if ( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } afterTransactionProcesses.register( e.getAfterTransactionCompletionProcess() ); @@ -657,7 +657,7 @@ public class ActionQueue { private void invalidateSpaces(String... spaces) { if ( spaces != null && spaces.length > 0 ) { for ( Serializable s : spaces ) { - if( afterTransactionProcesses == null ) { + if ( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } afterTransactionProcesses.addSpaceToInvalidate( (String) s ); @@ -691,21 +691,21 @@ public class ActionQueue { } public int numberOfCollectionRemovals() { - if( collectionRemovals == null ) { + if ( collectionRemovals == null ) { return 0; } return collectionRemovals.size(); } public int numberOfCollectionUpdates() { - if( collectionUpdates == null ) { + if ( collectionUpdates == null ) { return 0; } return collectionUpdates.size(); } public int numberOfCollectionCreations() { - if( collectionCreations == null ) { + if ( collectionCreations == null ) { return 0; } return collectionCreations.size(); @@ -718,24 +718,24 @@ public class ActionQueue { } public int numberOfUpdates() { - if( updates == null ) { + if ( updates == null ) { return 0; } return updates.size(); } public int numberOfInsertions() { - if( insertions == null ) { + if ( insertions == null ) { return 0; } return insertions.size(); } public TransactionCompletionProcesses getTransactionCompletionProcesses() { - if( beforeTransactionProcesses == null ) { + if ( beforeTransactionProcesses == null ) { beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); } - if( afterTransactionProcesses == null ) { + if ( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } return new TransactionCompletionProcesses( beforeTransactionProcesses, afterTransactionProcesses ); @@ -758,16 +758,16 @@ public class ActionQueue { public void sortCollectionActions() { if ( isOrderUpdatesEnabled() ) { // sort the updates by fk - if( collectionCreations != null ) { + if ( collectionCreations != null ) { collectionCreations.sort(); } - if( collectionUpdates != null ) { + if ( collectionUpdates != null ) { collectionUpdates.sort(); } - if( collectionQueuedOps != null ) { + if ( collectionQueuedOps != null ) { collectionQueuedOps.sort(); } - if( collectionRemovals != null ) { + if ( collectionRemovals != null ) { collectionRemovals.sort(); } } @@ -792,16 +792,16 @@ public class ActionQueue { } public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) { - if( collectionCreations != null ) { + if ( collectionCreations != null ) { collectionCreations.clear(); } - if( collectionUpdates != null ) { + if ( collectionUpdates != null ) { collectionUpdates.clear(); } - if( collectionQueuedOps != null ) { + if ( collectionQueuedOps != null ) { collectionQueuedOps.clear(); } - if( updates != null) { + if ( updates != null) { updates.clear(); } // collection deletions are a special case since update() can add @@ -835,19 +835,19 @@ public class ActionQueue { rescuedEntity = initializer.getImplementation( session ); } } - if( deletions != null ) { + if ( deletions != null ) { for ( int i = 0; i < deletions.size(); i++ ) { EntityDeleteAction action = deletions.get( i ); - if (action.getInstance() == rescuedEntity) { + if ( action.getInstance() == rescuedEntity ) { deletions.remove( i ); return; } } } - if( orphanRemovals != null ) { + if ( orphanRemovals != null ) { for ( int i = 0; i < orphanRemovals.size(); i++ ) { EntityDeleteAction action = orphanRemovals.get( i ); - if (action.getInstance() == rescuedEntity) { + if ( action.getInstance() == rescuedEntity ) { orphanRemovals.remove( i ); return; } @@ -864,14 +864,14 @@ public class ActionQueue { */ public void serialize(ObjectOutputStream oos) throws IOException { LOG.trace( "Serializing action-queue" ); - if( unresolvedInsertions == null ) { + if ( unresolvedInsertions == null ) { unresolvedInsertions = new UnresolvedEntityInsertActions(); } unresolvedInsertions.serialize( oos ); for ( ListProvider p : EXECUTABLE_LISTS_MAP.values() ) { ExecutableList l = p.get( this ); - if( l == null ) { + if ( l == null ) { oos.writeBoolean( false ); } else { @@ -902,8 +902,8 @@ public class ActionQueue { for ( ListProvider provider : EXECUTABLE_LISTS_MAP.values() ) { ExecutableList l = provider.get( rtn ); boolean notNull = ois.readBoolean(); - if( notNull ) { - if(l == null) { + if ( notNull ) { + if ( l == null ) { l = provider.init( rtn ); } l.readExternal( ois ); @@ -1218,7 +1218,7 @@ public class ActionQueue { BatchIdentifier nextBatchIdentifier = latestBatches.get( j ); if ( batchIdentifier.hasParent( nextBatchIdentifier ) ) { - if( nextBatchIdentifier.hasParent( batchIdentifier ) ) { + if ( nextBatchIdentifier.hasParent( batchIdentifier ) ) { //cycle detected, no need to continue break sort; } @@ -1232,7 +1232,7 @@ public class ActionQueue { } sorted = true; } - while ( !sorted && iterations <= maxIterations); + while ( !sorted && iterations <= maxIterations ); if ( iterations > maxIterations ) { LOG.warn( "The batch containing " + latestBatches.size() + " statements could not be sorted after " + maxIterations + " iterations. " + From d06588814029175c95dd901c93fa66c573925833 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sun, 18 Aug 2019 15:00:09 +0100 Subject: [PATCH 27/99] HHH-13589 Avoid HashMap.values() when forEach is an easy replacement --- .../main/java/org/hibernate/engine/spi/ActionQueue.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index 1e7ed134c5..8b7c3698f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -229,12 +229,12 @@ public class ActionQueue { } public void clear() { - for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) { + EXECUTABLE_LISTS_MAP.forEach( (k,listProvider) -> { ExecutableList l = listProvider.get( this ); if ( l != null ) { l.clear(); } - } + } ); if ( unresolvedInsertions != null ) { unresolvedInsertions.clear(); } @@ -472,12 +472,12 @@ public class ActionQueue { throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." ); } - for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) { + EXECUTABLE_LISTS_MAP.forEach( (k,listProvider) -> { ExecutableList l = listProvider.get( this ); if ( l != null && !l.isEmpty() ) { executeActions( l ); } - } + } ); } /** From eb675e1c7f27f9a086af5ed935d0d41d2afb817a Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 13:55:29 +0100 Subject: [PATCH 28/99] HHH-13587 Review formatting and logging style of StatefulPersistenceContext --- .../internal/StatefulPersistenceContext.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index b071c9a9f7..78c001a812 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -58,6 +58,7 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.ConcurrentReferenceHashMap; import org.hibernate.internal.util.collections.IdentityMap; +import org.hibernate.metamodel.spi.MetamodelImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; @@ -78,6 +79,7 @@ import org.jboss.logging.Logger; * their processing. * * @author Steve Ebersole + * @author Sanne Grinovero */ public class StatefulPersistenceContext implements PersistenceContext { private static final CoreMessageLogger LOG = Logger.getMessageLogger( @@ -199,7 +201,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void addUnownedCollection(CollectionKey key, PersistentCollection collection) { - if (unownedCollections==null) { + if ( unownedCollections == null ) { unownedCollections = new HashMap<>( INIT_COLL_SIZE ); } unownedCollections.put( key, collection ); @@ -212,8 +214,8 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public BatchFetchQueue getBatchFetchQueue() { - if (batchFetchQueue==null) { - batchFetchQueue = new BatchFetchQueue(this); + if ( batchFetchQueue == null ) { + batchFetchQueue = new BatchFetchQueue( this ); } return batchFetchQueue; } @@ -417,8 +419,8 @@ public class StatefulPersistenceContext implements PersistenceContext { entitySnapshotsByKey.remove( key ); nullifiableEntityKeys.remove( key ); if( batchFetchQueue != null ) { - getBatchFetchQueue().removeBatchLoadableEntityKey(key); - getBatchFetchQueue().removeSubselect(key); + getBatchFetchQueue().removeBatchLoadableEntityKey( key ); + getBatchFetchQueue().removeSubselect( key ); } return entity; } @@ -938,9 +940,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void initializeNonLazyCollections() throws HibernateException { if ( loadCounter == 0 ) { - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Initializing non-lazy collections" ); - } + LOG.trace( "Initializing non-lazy collections" ); //do this work only at the very highest level of the load //don't let this method be called recursively @@ -1278,8 +1278,9 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Object getIndexInOwner(String entity, String property, Object childEntity, Map mergeMap) { - final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( entity ); - final CollectionPersister cp = session.getFactory().getMetamodel().collectionPersister( entity + '.' + property ); + final MetamodelImplementor metamodel = session.getFactory().getMetamodel(); + final EntityPersister persister = metamodel.entityPersister( entity ); + final CollectionPersister cp = metamodel.collectionPersister( entity + '.' + property ); // try cache lookup first final Object parent = parentsByChild.get( childEntity ); @@ -1289,7 +1290,7 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( persister.isSubclassEntityName( entityEntry.getEntityName() ) ) { Object index = getIndexInParent( property, childEntity, persister, cp, parent ); - if (index==null && mergeMap!=null) { + if ( index == null && mergeMap != null ) { final Object unMergedInstance = mergeMap.get( parent ); final Object unMergedChild = mergeMap.get( childEntity ); if ( unMergedInstance != null && unMergedChild != null ) { @@ -1378,7 +1379,7 @@ public class StatefulPersistenceContext implements PersistenceContext { else { final EntityEntry ee = getEntry( entityOrProxy ); if ( ee == null ) { - throw new TransientObjectException("Instance was not associated with this persistence context" ); + throw new TransientObjectException( "Instance was not associated with this persistence context" ); } isReadOnly = ee.isReadOnly(); } @@ -1415,11 +1416,12 @@ public class StatefulPersistenceContext implements PersistenceContext { } private void setProxyReadOnly(HibernateProxy proxy, boolean readOnly) { - if ( proxy.getHibernateLazyInitializer().getSession() != getSession() ) { + final LazyInitializer hibernateLazyInitializer = proxy.getHibernateLazyInitializer(); + if ( hibernateLazyInitializer.getSession() != getSession() ) { throw new AssertionFailure( "Attempt to set a proxy to read-only that is associated with a different session" ); } - proxy.getHibernateLazyInitializer().setReadOnly( readOnly ); + hibernateLazyInitializer.setReadOnly( readOnly ); } private void setEntityReadOnly(Object entity, boolean readOnly) { @@ -1461,9 +1463,7 @@ public class StatefulPersistenceContext implements PersistenceContext { * @throws IOException serialization errors. */ public void serialize(ObjectOutputStream oos) throws IOException { - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Serializing persistence-context" ); - } + LOG.trace( "Serializing persistence-context" ); oos.writeBoolean( defaultReadOnly ); oos.writeBoolean( hasNonReadOnlyEntities ); @@ -1556,9 +1556,7 @@ public class StatefulPersistenceContext implements PersistenceContext { public static StatefulPersistenceContext deserialize( ObjectInputStream ois, SessionImplementor session) throws IOException, ClassNotFoundException { - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Deserializing persistence-context" ); - } + LOG.trace( "Deserializing persistence-context" ); final StatefulPersistenceContext rtn = new StatefulPersistenceContext( session ); SessionFactoryImplementor sfi = session.getFactory(); @@ -1848,7 +1846,7 @@ public class StatefulPersistenceContext implements PersistenceContext { new AfterTransactionCompletionProcess() { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { - if (success) { + if ( success ) { final boolean put = naturalIdCacheAccessStrategy.afterInsert( session, naturalIdCacheKey, id ); if ( put && statistics.isStatisticsEnabled() ) { statistics.naturalIdCachePut( @@ -1914,7 +1912,9 @@ public class StatefulPersistenceContext implements PersistenceContext { break; } default: { - LOG.debug( "Unexpected CachedNaturalIdValueSource [" + source + "]" ); + if ( LOG.isDebugEnabled() ) { + LOG.debug( "Unexpected CachedNaturalIdValueSource [" + source + "]" ); + } } } } From 5bbf417c523c938899a66ecf7b148b41fa880bb7 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 12:03:25 +0100 Subject: [PATCH 29/99] HHH-13587 Allocate StatefulPersistenceContext#nullifiableEntityKeys lazily --- .../engine/internal/AbstractEntityEntry.java | 3 +- .../engine/internal/ForeignKeys.java | 2 +- .../internal/StatefulPersistenceContext.java | 52 +++++++++++++++---- .../engine/spi/PersistenceContext.java | 25 +++++++++ .../internal/DefaultDeleteEventListener.java | 2 +- 5 files changed, 72 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java index c5045b4f46..f3c8e4f240 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java @@ -9,6 +9,7 @@ package org.hibernate.engine.internal; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.function.Supplier; import org.hibernate.AssertionFailure; import org.hibernate.CustomEntityDirtinessStrategy; @@ -309,7 +310,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry { return !isExistsInDatabase(); } else { - return session.getPersistenceContextInternal().getNullifiableEntityKeys().contains( getEntityKey() ); + return session.getPersistenceContextInternal().containsNullifiableEntityKey( this::getEntityKey ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ForeignKeys.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ForeignKeys.java index 860350e038..814cccf238 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/ForeignKeys.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/ForeignKeys.java @@ -160,7 +160,7 @@ public final class ForeignKeys { if ( isDelete && value == LazyPropertyInitializer.UNFETCHED_PROPERTY && type.isEntityType() && - !session.getPersistenceContextInternal().getNullifiableEntityKeys().isEmpty() ) { + !session.getPersistenceContextInternal().isNullifiableEntityKeysEmpty() ) { // IMPLEMENTATION NOTE: If cascade-remove was mapped for the attribute, // then value should have been initialized previously, when the remove operation was // cascaded to the property (because CascadingAction.DELETE.performOnLazyProperty() diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 78c001a812..969c246784 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; import org.hibernate.AssertionFailure; import org.hibernate.Hibernate; @@ -175,8 +176,6 @@ public class StatefulPersistenceContext implements PersistenceContext { collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); - nullifiableEntityKeys = new HashSet<>(); - nullAssociations = new HashSet<>( INIT_COLL_SIZE ); nonlazyCollections = new ArrayList<>( INIT_COLL_SIZE ); } @@ -255,7 +254,7 @@ public class StatefulPersistenceContext implements PersistenceContext { unownedCollections.clear(); } proxiesByKey.clear(); - nullifiableEntityKeys.clear(); + nullifiableEntityKeys = null; if ( batchFetchQueue != null ) { batchFetchQueue.clear(); } @@ -417,7 +416,9 @@ public class StatefulPersistenceContext implements PersistenceContext { // Clear all parent cache parentsByChild.clear(); entitySnapshotsByKey.remove( key ); - nullifiableEntityKeys.remove( key ); + if ( nullifiableEntityKeys != null ) { + nullifiableEntityKeys.remove( key ); + } if( batchFetchQueue != null ) { getBatchFetchQueue().removeBatchLoadableEntityKey( key ); getBatchFetchQueue().removeSubselect( key ); @@ -1027,6 +1028,9 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public HashSet getNullifiableEntityKeys() { + if ( nullifiableEntityKeys == null ) { + nullifiableEntityKeys = new HashSet<>(); + } return nullifiableEntityKeys; } @@ -1533,12 +1537,18 @@ public class StatefulPersistenceContext implements PersistenceContext { oos.writeObject( entry.getValue() ); } - oos.writeInt( nullifiableEntityKeys.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + nullifiableEntityKeys.size() + "] nullifiableEntityKey entries" ); + if ( nullifiableEntityKeys == null ) { + oos.writeInt( 0 ); } - for ( EntityKey entry : nullifiableEntityKeys ) { - entry.serialize( oos ); + else { + final int size = nullifiableEntityKeys.size(); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Starting serialization of [" + size + "] nullifiableEntityKey entries" ); + } + oos.writeInt( size ); + for ( EntityKey entry : nullifiableEntityKeys ) { + entry.serialize( oos ); + } } } @@ -1720,6 +1730,30 @@ public class StatefulPersistenceContext implements PersistenceContext { return false; } + @Override + public boolean containsNullifiableEntityKey(Supplier sek) { + if ( nullifiableEntityKeys == null || nullifiableEntityKeys.size() == 0 ) { + return false; + } + else { + final EntityKey entityKey = sek.get(); + return nullifiableEntityKeys.contains( entityKey ); + } + } + + @Override + public void registerNullifiableEntityKey(EntityKey key) { + if ( nullifiableEntityKeys == null ) { + nullifiableEntityKeys = new HashSet<>(); + } + this.nullifiableEntityKeys.add( key ); + } + + @Override + public boolean isNullifiableEntityKeysEmpty() { + return ( nullifiableEntityKeys == null || nullifiableEntityKeys.size() == 0 ); + } + private void cleanUpInsertedKeysAfterTransaction() { if ( insertedKeysMap != null ) { insertedKeysMap.clear(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index b3a4589aa1..52c25a4114 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -10,6 +10,7 @@ import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Map; +import java.util.function.Supplier; import org.hibernate.HibernateException; import org.hibernate.LockMode; @@ -477,7 +478,9 @@ public interface PersistenceContext { /** * Retrieve the set of EntityKeys representing nullifiable references + * @deprecated Use {@link #containsNullifiableEntityKey(Supplier)} or {@link #registerNullifiableEntityKey(EntityKey)} or {@link #isNullifiableEntityKeysEmpty()} */ + @Deprecated HashSet getNullifiableEntityKeys(); /** @@ -720,6 +723,28 @@ public interface PersistenceContext { */ boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id); + /** + * Checks if a certain {@link EntityKey} was registered as nullifiable on this {@link PersistenceContext}. + * + * @param sek a supplier for the EntityKey; this allows to not always needing to create the key; + * for example is the map is known to be empty there is no need to create one to check. + * @return true if the EntityKey had been registered before using {@link #registerNullifiableEntityKey(EntityKey)} + * @see #registerNullifiableEntityKey(EntityKey) + */ + boolean containsNullifiableEntityKey(Supplier sek); + + /** + * Registers an {@link EntityKey} as nullifiable on this {@link PersistenceContext}. + * @param key + */ + void registerNullifiableEntityKey(EntityKey key); + + /** + * @return true if no {@link EntityKey} was registered as nullifiable on this {@link PersistenceContext}. + * @see #registerNullifiableEntityKey(EntityKey) + */ + boolean isNullifiableEntityKeysEmpty(); + /** * Provides centralized access to natural-id-related functionality. */ diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java index c1616245bc..eb09b3bf02 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java @@ -287,7 +287,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback new ForeignKeys.Nullifier( entity, true, false, session, persister ).nullifyTransientReferences( entityEntry.getDeletedState() ); new Nullability( session ).checkNullability( entityEntry.getDeletedState(), persister, Nullability.NullabilityCheckType.DELETE ); - persistenceContext.getNullifiableEntityKeys().add( key ); + persistenceContext.registerNullifiableEntityKey( key ); if ( isOrphanRemovalBeforeUpdates ) { // TODO: The removeOrphan concept is a temporary "hack" for HHH-6484. This should be removed once action/task From aae670b9b33dca97ac9624eac3e62ca23532663c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 15:53:07 +0100 Subject: [PATCH 30/99] HHH-13587 Allocate StatefulPersistenceContext#nullAssociations lazily --- .../engine/internal/StatefulPersistenceContext.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 969c246784..b8cde15c70 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -176,7 +176,6 @@ public class StatefulPersistenceContext implements PersistenceContext { collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); - nullAssociations = new HashSet<>( INIT_COLL_SIZE ); nonlazyCollections = new ArrayList<>( INIT_COLL_SIZE ); } @@ -1359,16 +1358,19 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void addNullProperty(EntityKey ownerKey, String propertyName) { + if ( nullAssociations == null ) { + nullAssociations = new HashSet<>( INIT_COLL_SIZE ); + } nullAssociations.add( new AssociationKey( ownerKey, propertyName ) ); } @Override public boolean isPropertyNull(EntityKey ownerKey, String propertyName) { - return nullAssociations.contains( new AssociationKey( ownerKey, propertyName ) ); + return nullAssociations != null && nullAssociations.contains( new AssociationKey( ownerKey, propertyName ) ); } private void clearNullProperties() { - nullAssociations.clear(); + nullAssociations = null; } @Override From 0538b97d6a087f77e2ca51248ef3317c6f0973a8 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 15:58:36 +0100 Subject: [PATCH 31/99] HHH-13587 Allocate StatefulPersistenceContext#naturalIdXrefDelegate lazily --- .../internal/StatefulPersistenceContext.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index b8cde15c70..0a681cf470 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -262,7 +262,7 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( loadContexts != null ) { loadContexts.cleanup(); } - naturalIdXrefDelegate.clear(); + naturalIdXrefDelegate = null; } @Override @@ -1762,11 +1762,16 @@ public class StatefulPersistenceContext implements PersistenceContext { } } - - // NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - private final NaturalIdXrefDelegate naturalIdXrefDelegate = new NaturalIdXrefDelegate( this ); + private NaturalIdXrefDelegate naturalIdXrefDelegate; + + private NaturalIdXrefDelegate getNaturalIdXrefDelegate() { + if ( naturalIdXrefDelegate == null ) { + this.naturalIdXrefDelegate = new NaturalIdXrefDelegate( this ); + } + return naturalIdXrefDelegate; + } private final NaturalIdHelper naturalIdHelper = new NaturalIdHelper() { @Override @@ -1785,7 +1790,7 @@ public class StatefulPersistenceContext implements PersistenceContext { // from a single load event. The first put journal would come from the natural id resolution; // the second comes from the entity loading. In this condition, we want to avoid the multiple // 'put' stats incrementing. - final boolean justAddedLocally = naturalIdXrefDelegate.cacheNaturalIdCrossReference( persister, id, naturalIdValues ); + final boolean justAddedLocally = getNaturalIdXrefDelegate().cacheNaturalIdCrossReference( persister, id, naturalIdValues ); if ( justAddedLocally && persister.hasNaturalIdCache() ) { managedSharedCacheEntries( persister, id, naturalIdValues, null, CachedNaturalIdValueSource.LOAD ); @@ -1808,7 +1813,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final Object[] naturalIdValues = extractNaturalIdValues( state, persister ); // cache - naturalIdXrefDelegate.cacheNaturalIdCrossReference( persister, id, naturalIdValues ); + getNaturalIdXrefDelegate().cacheNaturalIdCrossReference( persister, id, naturalIdValues ); } @Override @@ -1965,7 +1970,7 @@ public class StatefulPersistenceContext implements PersistenceContext { persister = locateProperPersister( persister ); final Object[] naturalIdValues = getNaturalIdValues( state, persister ); - final Object[] localNaturalIdValues = naturalIdXrefDelegate.removeNaturalIdCrossReference( + final Object[] localNaturalIdValues = getNaturalIdXrefDelegate().removeNaturalIdCrossReference( persister, id, naturalIdValues @@ -2004,12 +2009,12 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) { - return naturalIdXrefDelegate.findCachedNaturalId( locateProperPersister( persister ), pk ); + return getNaturalIdXrefDelegate().findCachedNaturalId( locateProperPersister( persister ), pk ); } @Override public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) { - return naturalIdXrefDelegate.findCachedNaturalIdResolution( locateProperPersister( persister ), naturalIdValues ); + return getNaturalIdXrefDelegate().findCachedNaturalIdResolution( locateProperPersister( persister ), naturalIdValues ); } @Override @@ -2047,7 +2052,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Collection getCachedPkResolutions(EntityPersister entityPersister) { - return naturalIdXrefDelegate.getCachedPkResolutions( entityPersister ); + return getNaturalIdXrefDelegate().getCachedPkResolutions( entityPersister ); } @Override @@ -2060,6 +2065,7 @@ public class StatefulPersistenceContext implements PersistenceContext { persister = locateProperPersister( persister ); final Object[] naturalIdValuesFromCurrentObjectState = extractNaturalIdValues( entity, persister ); + final NaturalIdXrefDelegate naturalIdXrefDelegate = getNaturalIdXrefDelegate(); final boolean changed = ! naturalIdXrefDelegate.sameAsCached( persister, pk, @@ -2081,12 +2087,12 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void cleanupFromSynchronizations() { - naturalIdXrefDelegate.unStashInvalidNaturalIdReferences(); + getNaturalIdXrefDelegate().unStashInvalidNaturalIdReferences(); } @Override public void handleEviction(Object object, EntityPersister persister, Serializable identifier) { - naturalIdXrefDelegate.removeNaturalIdCrossReference( + getNaturalIdXrefDelegate().removeNaturalIdCrossReference( persister, identifier, findCachedNaturalId( persister, identifier ) From a11359524b5325834c4c2ac673f15a7a7004d9a0 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 16:17:17 +0100 Subject: [PATCH 32/99] HHH-13587 Lazily initialize ConcurrentReferenceHashMap proxiesByKey in StatefulPersistenceContext --- .../internal/StatefulPersistenceContext.java | 93 ++++++++++--------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 0a681cf470..2aed5a2673 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -157,15 +157,6 @@ public class StatefulPersistenceContext implements PersistenceContext { entitiesByKey = new HashMap<>( INIT_COLL_SIZE ); entitiesByUniqueKey = new HashMap<>( INIT_COLL_SIZE ); - //noinspection unchecked - proxiesByKey = new ConcurrentReferenceHashMap<>( - INIT_COLL_SIZE, - .75f, - 1, - ConcurrentReferenceHashMap.ReferenceType.STRONG, - ConcurrentReferenceHashMap.ReferenceType.WEAK, - null - ); entitySnapshotsByKey = new HashMap<>( INIT_COLL_SIZE ); entityEntryContext = new EntityEntryContext( this ); @@ -179,6 +170,21 @@ public class StatefulPersistenceContext implements PersistenceContext { nonlazyCollections = new ArrayList<>( INIT_COLL_SIZE ); } + private ConcurrentMap getOrInitializeProxiesByKey() { + if ( proxiesByKey == null ) { + //noinspection unchecked + proxiesByKey = new ConcurrentReferenceHashMap<>( + INIT_COLL_SIZE, + .75f, + 1, + ConcurrentReferenceHashMap.ReferenceType.STRONG, + ConcurrentReferenceHashMap.ReferenceType.WEAK, + null + ); + } + return proxiesByKey; + } + @Override public boolean isStateless() { return false; @@ -220,12 +226,12 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void clear() { - for ( Object o : proxiesByKey.values() ) { - if ( o == null ) { - //entry may be GCd - continue; - } - ((HibernateProxy) o).getHibernateLazyInitializer().unsetSession(); + if ( proxiesByKey != null ) { + proxiesByKey.forEach( (k,o) -> { + if ( o != null) { + ((HibernateProxy) o).getHibernateLazyInitializer().unsetSession(); + } + } ); } for ( Entry objectEntityEntryEntry : entityEntryContext.reentrantSafeEntityEntries() ) { @@ -252,7 +258,7 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( unownedCollections != null ) { unownedCollections.clear(); } - proxiesByKey.clear(); + proxiesByKey = null; nullifiableEntityKeys = null; if ( batchFetchQueue != null ) { batchFetchQueue.clear(); @@ -566,7 +572,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public boolean containsProxy(Object entity) { - return proxiesByKey.containsValue( entity ); + return proxiesByKey != null && proxiesByKey.containsValue( entity ); } @Override @@ -618,7 +624,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( li.getEntityName() ); final EntityKey key = session.generateEntityKey( li.getIdentifier(), persister ); // any earlier proxy takes precedence - proxiesByKey.putIfAbsent( key, proxy ); + getOrInitializeProxiesByKey().putIfAbsent( key, proxy ); proxy.getHibernateLazyInitializer().setSession( session ); } } @@ -688,7 +694,7 @@ public class StatefulPersistenceContext implements PersistenceContext { // If an impl is passed, there is really no point in creating a proxy. // It would just be extra processing. Just return the impl if ( object != null ) { - proxiesByKey.remove( key ); + removeProxyByKey( key ); return object; } @@ -699,7 +705,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final Object impl = originalHibernateProxy.getHibernateLazyInitializer().getImplementation(); // can we return it? if ( concreteProxyClass.isInstance( impl ) ) { - proxiesByKey.remove( key ); + removeProxyByKey( key ); return impl; } } @@ -724,12 +730,19 @@ public class StatefulPersistenceContext implements PersistenceContext { } } + private Object removeProxyByKey(final EntityKey key) { + if ( proxiesByKey != null ) { + return proxiesByKey.remove( key ); + } + return null; + } + @Override public Object proxyFor(EntityPersister persister, EntityKey key, Object impl) throws HibernateException { if ( !persister.hasProxy() ) { return impl; } - final Object proxy = proxiesByKey.get( key ); + final Object proxy = getProxy( key ); return ( proxy != null ) ? narrowProxy( proxy, persister, key, impl ) : impl; } @@ -1008,12 +1021,12 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Object getProxy(EntityKey key) { - return proxiesByKey.get( key ); + return proxiesByKey == null ? null : proxiesByKey.get( key ); } @Override public void addProxy(EntityKey key, Object proxy) { - proxiesByKey.put( key, proxy ); + getOrInitializeProxiesByKey().put( key, proxy ); } @Override @@ -1022,7 +1035,7 @@ public class StatefulPersistenceContext implements PersistenceContext { batchFetchQueue.removeBatchLoadableEntityKey( key ); batchFetchQueue.removeSubselect( key ); } - return proxiesByKey.remove( key ); + return removeProxyByKey( key ); } @Override @@ -1038,10 +1051,6 @@ public class StatefulPersistenceContext implements PersistenceContext { return entitiesByKey; } - public Map getProxiesByKey() { - return proxiesByKey; - } - @Override public int getNumberOfManagedEntities() { return entityEntryContext.getNumberOfManagedEntities(); @@ -1492,13 +1501,18 @@ public class StatefulPersistenceContext implements PersistenceContext { oos.writeObject( entry.getValue() ); } - oos.writeInt( proxiesByKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + proxiesByKey.size() + "] proxiesByKey entries" ); + if ( proxiesByKey == null ) { + oos.writeInt( 0 ); } - for ( Map.Entry entry : proxiesByKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); + else { + oos.writeInt( proxiesByKey.size() ); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Starting serialization of [" + proxiesByKey.size() + "] proxiesByKey entries" ); + } + for ( Map.Entry entry : proxiesByKey.entrySet() ) { + entry.getKey().serialize( oos ); + oos.writeObject( entry.getValue() ); + } } oos.writeInt( entitySnapshotsByKey.size() ); @@ -1604,21 +1618,12 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( LOG.isTraceEnabled() ) { LOG.trace( "Starting deserialization of [" + count + "] proxiesByKey entries" ); } - //noinspection unchecked - rtn.proxiesByKey = new ConcurrentReferenceHashMap<>( - count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count, - .75f, - 1, - ConcurrentReferenceHashMap.ReferenceType.STRONG, - ConcurrentReferenceHashMap.ReferenceType.WEAK, - null - ); for ( int i = 0; i < count; i++ ) { final EntityKey ek = EntityKey.deserialize( ois, sfi ); final Object proxy = ois.readObject(); if ( proxy instanceof HibernateProxy ) { ( (HibernateProxy) proxy ).getHibernateLazyInitializer().setSession( session ); - rtn.proxiesByKey.put( ek, proxy ); + rtn.getOrInitializeProxiesByKey().put( ek, proxy ); } else { // otherwise, the proxy was pruned during the serialization process From 1101727a4f7584944500d35f7105393c24f7c23d Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 16:43:15 +0100 Subject: [PATCH 33/99] HHH-13587 StatefulPersistenceContext#unownedCollections was almost fully lazy already --- .../hibernate/engine/internal/StatefulPersistenceContext.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 2aed5a2673..49c32a27bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -255,9 +255,7 @@ public class StatefulPersistenceContext implements PersistenceContext { entitySnapshotsByKey.clear(); collectionsByKey.clear(); collectionEntries.clear(); - if ( unownedCollections != null ) { - unownedCollections.clear(); - } + unownedCollections = null; proxiesByKey = null; nullifiableEntityKeys = null; if ( batchFetchQueue != null ) { From 0d10174c236d4d73d7f64893819687a663ce1be7 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 16:53:01 +0100 Subject: [PATCH 34/99] HHH-13587 Make StatefulPersistenceContext#parentsByChild a lazily initialized IdentityHashMap --- .../internal/StatefulPersistenceContext.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 49c32a27bd..c9df3b968a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -162,7 +162,6 @@ public class StatefulPersistenceContext implements PersistenceContext { entityEntryContext = new EntityEntryContext( this ); // entityEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); - parentsByChild = new IdentityHashMap<>( INIT_COLL_SIZE ); collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); @@ -250,8 +249,7 @@ public class StatefulPersistenceContext implements PersistenceContext { entitiesByKey.clear(); entitiesByUniqueKey.clear(); entityEntryContext.clear(); -// entityEntries.clear(); - parentsByChild.clear(); + parentsByChild = null; entitySnapshotsByKey.clear(); collectionsByKey.clear(); collectionEntries.clear(); @@ -417,7 +415,7 @@ public class StatefulPersistenceContext implements PersistenceContext { } } // Clear all parent cache - parentsByChild.clear(); + parentsByChild = null; entitySnapshotsByKey.remove( key ); if ( nullifiableEntityKeys != null ) { nullifiableEntityKeys.remove( key ); @@ -1173,7 +1171,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final CollectionPersister collectionPersister = session.getFactory().getMetamodel().collectionPersister( collectionRole ); // try cache lookup first - final Object parent = parentsByChild.get( childEntity ); + final Object parent = getParentsByChild( childEntity ); if ( parent != null ) { final EntityEntry entityEntry = entityEntryContext.getEntityEntry( parent ); //there maybe more than one parent, filter by type @@ -1183,7 +1181,7 @@ public class StatefulPersistenceContext implements PersistenceContext { } else { // remove wrong entry - parentsByChild.remove( childEntity ); + removeChildParent( childEntity ); } } @@ -1274,6 +1272,13 @@ public class StatefulPersistenceContext implements PersistenceContext { return null; } + private Object getParentsByChild(Object childEntity) { + if ( parentsByChild != null ) { + parentsByChild.get( childEntity ); + } + return null; + } + private boolean isFoundInParent( String property, Object childEntity, @@ -1293,7 +1298,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final CollectionPersister cp = metamodel.collectionPersister( entity + '.' + property ); // try cache lookup first - final Object parent = parentsByChild.get( childEntity ); + final Object parent = getParentsByChild( childEntity ); if ( parent != null ) { final EntityEntry entityEntry = entityEntryContext.getEntityEntry( parent ); //there maybe more than one parent, filter by type @@ -1317,7 +1322,7 @@ public class StatefulPersistenceContext implements PersistenceContext { } else { // remove wrong entry - parentsByChild.remove( childEntity ); + removeChildParent( childEntity ); } } @@ -1450,7 +1455,7 @@ public class StatefulPersistenceContext implements PersistenceContext { public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) { final Object entity = entitiesByKey.remove( oldKey ); final EntityEntry oldEntry = entityEntryContext.removeEntityEntry( entity ); - parentsByChild.clear(); + this.parentsByChild = null; final EntityKey newKey = session.generateEntityKey( generatedId, oldEntry.getPersister() ); addEntity( newKey, entity ); @@ -1691,15 +1696,19 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void addChildParent(Object child, Object parent) { + if ( parentsByChild == null ) { + parentsByChild = new IdentityHashMap<>( INIT_COLL_SIZE ); + } parentsByChild.put( child, parent ); } @Override public void removeChildParent(Object child) { - parentsByChild.remove( child ); + if ( parentsByChild != null ) { + parentsByChild.remove( child ); + } } - // INSERTED KEYS HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private HashMap> insertedKeysMap; From 3b30c60ce3e81b23c3eea21018d5831b8bb4c5ad Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 17:00:13 +0100 Subject: [PATCH 35/99] HHH-13587 Introduce SPI method PersistenceContext#getCollectionEntriesSize() --- .../internal/StatefulPersistenceContext.java | 5 +++++ .../engine/spi/PersistenceContext.java | 7 +++++++ .../AbstractFlushingEventListener.java | 2 +- .../DefaultAutoFlushEventListener.java | 2 +- .../internal/DefaultFlushEventListener.java | 2 +- .../stat/internal/SessionStatisticsImpl.java | 2 +- .../TestAutoFlushBeforeQueryExecution.java | 18 +++++++++--------- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index c9df3b968a..9ea1d7d22a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -1768,6 +1768,11 @@ public class StatefulPersistenceContext implements PersistenceContext { return ( nullifiableEntityKeys == null || nullifiableEntityKeys.size() == 0 ); } + @Override + public int getCollectionEntriesSize() { + return collectionEntries == null ? 0 : collectionEntries.size(); + } + private void cleanUpInsertedKeysAfterTransaction() { if ( insertedKeysMap != null ) { insertedKeysMap.clear(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 52c25a4114..7c687a771a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -745,6 +745,13 @@ public interface PersistenceContext { */ boolean isNullifiableEntityKeysEmpty(); + /** + * The size of the internal map storing all collection entries. + * (The map is not exposed directly, but the size is often useful) + * @return the size + */ + int getCollectionEntriesSize(); + /** * Provides centralized access to natural-id-related functionality. */ diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java index fcbc02712e..701688a6a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java @@ -125,7 +125,7 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi session.getActionQueue().numberOfCollectionCreations(), session.getActionQueue().numberOfCollectionUpdates(), session.getActionQueue().numberOfCollectionRemovals(), - persistenceContext.getCollectionEntries().size() + persistenceContext.getCollectionEntriesSize() ); new EntityPrinter( session.getFactory() ).toString( persistenceContext.getEntitiesByKey().entrySet() diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java index 365a010bee..3683bbfd9f 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java @@ -89,6 +89,6 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener return !source.getHibernateFlushMode().lessThan( FlushMode.AUTO ) && source.getDontFlushFromFind() == 0 && ( persistenceContext.getNumberOfManagedEntities() > 0 || - persistenceContext.getCollectionEntries().size() > 0 ); + persistenceContext.getCollectionEntriesSize() > 0 ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java index fc8889d5d2..4d32fa7bfc 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java @@ -31,7 +31,7 @@ public class DefaultFlushEventListener extends AbstractFlushingEventListener imp final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); if ( persistenceContext.getNumberOfManagedEntities() > 0 || - persistenceContext.getCollectionEntries().size() > 0 ) { + persistenceContext.getCollectionEntriesSize() > 0 ) { try { source.getEventListenerManager().flushStart(); diff --git a/hibernate-core/src/main/java/org/hibernate/stat/internal/SessionStatisticsImpl.java b/hibernate-core/src/main/java/org/hibernate/stat/internal/SessionStatisticsImpl.java index 3eb1055513..fd6da5cca8 100755 --- a/hibernate-core/src/main/java/org/hibernate/stat/internal/SessionStatisticsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/stat/internal/SessionStatisticsImpl.java @@ -28,7 +28,7 @@ public class SessionStatisticsImpl implements SessionStatistics { } public int getCollectionCount() { - return session.getPersistenceContextInternal().getCollectionEntries().size(); + return session.getPersistenceContextInternal().getCollectionEntriesSize(); } public Set getEntityKeys() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/flush/TestAutoFlushBeforeQueryExecution.java b/hibernate-core/src/test/java/org/hibernate/test/flush/TestAutoFlushBeforeQueryExecution.java index 7d2ec3cd30..1a0631e80f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/flush/TestAutoFlushBeforeQueryExecution.java +++ b/hibernate-core/src/test/java/org/hibernate/test/flush/TestAutoFlushBeforeQueryExecution.java @@ -60,7 +60,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas final PersistenceContext persistenceContext = ( (SessionImplementor) s ).getPersistenceContext(); final ActionQueue actionQueue = ( (SessionImpl) s ).getActionQueue(); - assertEquals( 1, persistenceContext.getCollectionEntries().size() ); + assertEquals( 1, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionsByKey().values().contains( publisher.getAuthors() ) ); @@ -73,7 +73,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas "autoflush collection update", s.createQuery( "select a from Publisher p join p.authors a" ).list().size() == 1 ); - assertEquals( 2, persistenceContext.getCollectionEntries().size() ); + assertEquals( 2, persistenceContext.getCollectionEntriesSize() ); assertEquals( 2, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author1.getBooks() ) ); @@ -88,7 +88,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas assertTrue( "autoflush collection update", s.createQuery( "select a from Publisher p join p.authors a" ).list().size() == 0 ); - assertEquals( 1, persistenceContext.getCollectionEntries().size() ); + assertEquals( 1, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionsByKey().values().contains( publisher.getAuthors() ) ); @@ -102,7 +102,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas publisher.getAuthors().add( author2 ); List results = s.createQuery( "select a from Publisher p join p.authors a" ).list(); assertEquals( 1, results.size() ); - assertEquals( 2, persistenceContext.getCollectionEntries().size() ); + assertEquals( 2, persistenceContext.getCollectionEntriesSize() ); assertEquals( 2, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author2.getBooks() ) ); @@ -139,7 +139,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas final PersistenceContext persistenceContext = ( (SessionImplementor) s ).getPersistenceContext(); final ActionQueue actionQueue = ( (SessionImpl) s ).getActionQueue(); - assertEquals( 1, persistenceContext.getCollectionEntries().size() ); + assertEquals( 1, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionsByKey().values().contains( publisher.getAuthors() ) ); @@ -149,7 +149,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas author1.setPublisher( publisher ); publisher.getAuthors().add( author1 ); assertTrue( s.createQuery( "from UnrelatedEntity" ).list().size() == 1 ); - assertEquals( 2, persistenceContext.getCollectionEntries().size() ); + assertEquals( 2, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author1.getBooks() ) ); @@ -161,7 +161,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas publisher.getAuthors().clear(); assertEquals( 0, actionQueue.numberOfCollectionRemovals() ); assertTrue( s.createQuery( "from UnrelatedEntity" ).list().size() == 1 ); - assertEquals( 2, persistenceContext.getCollectionEntries().size() ); + assertEquals( 2, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author1.getBooks() ) ); @@ -176,7 +176,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas publisher.getAuthors().add( author2 ); List results = s.createQuery( "from UnrelatedEntity" ).list(); assertEquals( 1, results.size() ); - assertEquals( 4, persistenceContext.getCollectionEntries().size() ); + assertEquals( 4, persistenceContext.getCollectionEntriesSize() ); assertEquals( 1, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author2.getBooks() ) ); @@ -186,7 +186,7 @@ public class TestAutoFlushBeforeQueryExecution extends BaseCoreFunctionalTestCas assertEquals( 0, actionQueue.numberOfCollectionRemovals() ); s.flush(); - assertEquals( 2, persistenceContext.getCollectionEntries().size() ); + assertEquals( 2, persistenceContext.getCollectionEntriesSize() ); assertEquals( 2, persistenceContext.getCollectionsByKey().size() ); assertTrue( persistenceContext.getCollectionEntries().containsKey( publisher.getAuthors() ) ); assertTrue( persistenceContext.getCollectionEntries().containsKey( author2.getBooks() ) ); From 690a8d55207c2ca5a195508daaeac675ef93c54e Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 17:07:20 +0100 Subject: [PATCH 36/99] HHH-13587 Make StatefulPersistenceContext#collectionEntries also lazily initialized --- .../internal/StatefulPersistenceContext.java | 83 +++++++-- .../engine/spi/PersistenceContext.java | 17 ++ .../AbstractFlushingEventListener.java | 176 ++++++++---------- .../event/internal/EvictVisitor.java | 2 +- .../util/collections/IdentityMap.java | 10 + 5 files changed, 172 insertions(+), 116 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 9ea1d7d22a..b072382e11 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; import java.util.function.Supplier; import org.hibernate.AssertionFailure; @@ -160,9 +161,6 @@ public class StatefulPersistenceContext implements PersistenceContext { entitySnapshotsByKey = new HashMap<>( INIT_COLL_SIZE ); entityEntryContext = new EntityEntryContext( this ); -// entityEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); - collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); - collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); @@ -243,7 +241,9 @@ public class StatefulPersistenceContext implements PersistenceContext { } final SharedSessionContractImplementor session = getSession(); - IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) ); + if ( collectionEntries != null ) { + IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) ); + } arrayHolders.clear(); entitiesByKey.clear(); @@ -252,7 +252,7 @@ public class StatefulPersistenceContext implements PersistenceContext { parentsByChild = null; entitySnapshotsByKey.clear(); collectionsByKey.clear(); - collectionEntries.clear(); + collectionEntries = null; unownedCollections = null; proxiesByKey = null; nullifiableEntityKeys = null; @@ -454,7 +454,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public CollectionEntry getCollectionEntry(PersistentCollection coll) { - return collectionEntries.get( coll ); + return collectionEntries == null ? null : collectionEntries.get( coll ); } @Override @@ -563,7 +563,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public boolean containsCollection(PersistentCollection collection) { - return collectionEntries.containsKey( collection ); + return collectionEntries != null && collectionEntries.containsKey( collection ); } @Override @@ -888,7 +888,7 @@ public class StatefulPersistenceContext implements PersistenceContext { * @param key The key of the collection's entry. */ private void addCollection(PersistentCollection coll, CollectionEntry entry, Serializable key) { - collectionEntries.put( coll, entry ); + getOrInitializeCollectionEntries().put( coll, entry ); final CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key ); final PersistentCollection old = collectionsByKey.put( collectionKey, coll ); if ( old != null ) { @@ -897,12 +897,21 @@ public class StatefulPersistenceContext implements PersistenceContext { } // or should it actually throw an exception? old.unsetSession( session ); - collectionEntries.remove( old ); + if ( collectionEntries != null ) { + collectionEntries.remove( old ); + } // watch out for a case where old is still referenced // somewhere in the object graph! (which is a user error) } } + private IdentityMap getOrInitializeCollectionEntries() { + if ( this.collectionEntries == null ) { + this.collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); + } + return this.collectionEntries; + } + /** * Add a collection to the cache, creating a new collection entry for it * @@ -911,7 +920,7 @@ public class StatefulPersistenceContext implements PersistenceContext { */ private void addCollection(PersistentCollection collection, CollectionPersister persister) { final CollectionEntry ce = new CollectionEntry( persister, collection ); - collectionEntries.put( collection, ce ); + getOrInitializeCollectionEntries().put( collection, ce ); } @Override @@ -998,7 +1007,7 @@ public class StatefulPersistenceContext implements PersistenceContext { } else { coll = getCollectionHolder( collection ); - if ( coll == null ) { + if ( coll == null && collectionEntries != null ) { //it might be an unwrapped collection reference! //try to find a wrapper (slowish) final Iterator wrappers = collectionEntries.keyIterator(); @@ -1057,9 +1066,29 @@ public class StatefulPersistenceContext implements PersistenceContext { return null; } + /** + * @deprecated We should not expose this directly: the other accessors that have been created as a replacement + * have better chances of skipping initializing this map, which is a good performance improvement. + * @return the map of managed collection entries. + */ @Override + @Deprecated public Map getCollectionEntries() { - return collectionEntries; + return getOrInitializeCollectionEntries(); + } + + @Override + public void forEachCollectionEntry(BiConsumer action, boolean concurrent) { + if ( collectionEntries != null ) { + if ( concurrent ) { + for ( Map.Entry entry : IdentityMap.concurrentEntries( collectionEntries ) ) { + action.accept( entry.getKey(), entry.getValue() ); + } + } + else { + collectionEntries.forEach( action ); + } + } } @Override @@ -1538,13 +1567,18 @@ public class StatefulPersistenceContext implements PersistenceContext { oos.writeObject( entry.getValue() ); } - oos.writeInt( collectionEntries.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + collectionEntries.size() + "] collectionEntries entries" ); + if ( collectionEntries == null ) { + oos.writeInt( 0 ); } - for ( Map.Entry entry : collectionEntries.entrySet() ) { - oos.writeObject( entry.getKey() ); - entry.getValue().serialize( oos ); + else { + oos.writeInt( collectionEntries.size() ); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Starting serialization of [" + collectionEntries.size() + "] collectionEntries entries" ); + } + for ( Map.Entry entry : collectionEntries.entrySet() ) { + oos.writeObject( entry.getKey() ); + entry.getValue().serialize( oos ); + } } oos.writeInt( arrayHolders.size() ); @@ -1660,12 +1694,11 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( LOG.isTraceEnabled() ) { LOG.trace( "Starting deserialization of [" + count + "] collectionEntries entries" ); } - rtn.collectionEntries = IdentityMap.instantiateSequenced( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count ); for ( int i = 0; i < count; i++ ) { final PersistentCollection pc = (PersistentCollection) ois.readObject(); final CollectionEntry ce = CollectionEntry.deserialize( ois, session ); pc.setCurrentSession( session ); - rtn.collectionEntries.put( pc, ce ); + rtn.getOrInitializeCollectionEntries().put( pc, ce ); } count = ois.readInt(); @@ -1773,6 +1806,16 @@ public class StatefulPersistenceContext implements PersistenceContext { return collectionEntries == null ? 0 : collectionEntries.size(); } + @Override + public CollectionEntry removeCollectionEntry(PersistentCollection collection) { + if ( collectionEntries == null ) { + return null; + } + else { + return collectionEntries.remove( collection ); + } + } + private void cleanUpInsertedKeysAfterTransaction() { if ( insertedKeysMap != null ) { insertedKeysMap.clear(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 7c687a771a..44057ed85e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -10,6 +10,7 @@ import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.Supplier; import org.hibernate.HibernateException; @@ -510,9 +511,18 @@ public interface PersistenceContext { /** * Get the mapping from collection instance to collection entry + * @deprecated use {@link #removeCollectionEntry(PersistentCollection)} or {@link #getCollectionEntriesSize()}, {@link #forEachCollectionEntry(BiConsumer,boolean)}. */ + @Deprecated Map getCollectionEntries(); + /** + * Execute some action on each entry of the collectionEntries map, optionally iterating on a defensive copy. + * @param action the lambda to apply on each PersistentCollection,CollectionEntry map entry of the PersistenceContext. + * @param concurrent set this to false for improved efficiency, but that would make it illegal to make changes to the underlying collectionEntries map. + */ + void forEachCollectionEntry(BiConsumer action, boolean concurrent); + /** * Get the mapping from collection key to collection instance */ @@ -752,6 +762,13 @@ public interface PersistenceContext { */ int getCollectionEntriesSize(); + /** + * Remove a {@link PersistentCollection} from the {@link PersistenceContext}. + * @param collection the collection to remove + * @return the matching {@link CollectionEntry}, if any was removed. + */ + CollectionEntry removeCollectionEntry(PersistentCollection collection); + /** * Provides centralized access to natural-id-related functionality. */ diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java index 701688a6a1..b3120d7c5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java @@ -11,11 +11,11 @@ import java.util.IdentityHashMap; import java.util.Map; import org.hibernate.HibernateException; +import org.hibernate.Interceptor; import org.hibernate.action.internal.CollectionRecreateAction; import org.hibernate.action.internal.CollectionRemoveAction; import org.hibernate.action.internal.CollectionUpdateAction; import org.hibernate.action.internal.QueuedOperationCollectionAction; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; import org.hibernate.engine.internal.Collections; @@ -23,7 +23,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingActions; -import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; @@ -38,7 +37,6 @@ import org.hibernate.event.spi.FlushEntityEventListener; import org.hibernate.event.spi.FlushEvent; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.EntityPrinter; -import org.hibernate.internal.util.collections.IdentityMap; import org.hibernate.internal.util.collections.LazyIterator; import org.hibernate.persister.entity.EntityPersister; @@ -193,11 +191,9 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi // and reset reached, doupdate, etc. LOG.debug( "Dirty checking collections" ); - - for ( Map.Entry entry : - IdentityMap.concurrentEntries( (Map) persistenceContext.getCollectionEntries() ) ) { - entry.getValue().preFlush( entry.getKey() ); - } + persistenceContext.forEachCollectionEntry( (pc,ce) -> { + ce.preFlush( pc ); + }, true ); } /** @@ -252,78 +248,70 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi private int flushCollections(final EventSource session, final PersistenceContext persistenceContext) throws HibernateException { LOG.trace( "Processing unreferenced collections" ); - final Map.Entry[] entries = IdentityMap.concurrentEntries( - (Map) persistenceContext.getCollectionEntries() - ); + final int count = persistenceContext.getCollectionEntriesSize(); - final int count = entries.length; - - for ( Map.Entry me : entries ) { - CollectionEntry ce = me.getValue(); - if ( !ce.isReached() && !ce.isIgnore() ) { - Collections.processUnreachableCollection( me.getKey(), session ); - } - } + persistenceContext.forEachCollectionEntry( + (persistentCollection, collectionEntry) -> { + if ( !collectionEntry.isReached() && !collectionEntry.isIgnore() ) { + Collections.processUnreachableCollection( persistentCollection, session ); + } + }, true ); // Schedule updates to collections: LOG.trace( "Scheduling collection removes/(re)creates/updates" ); - ActionQueue actionQueue = session.getActionQueue(); - for ( Map.Entry me : - IdentityMap.concurrentEntries( (Map) persistenceContext.getCollectionEntries() ) ) { - PersistentCollection coll = me.getKey(); - CollectionEntry ce = me.getValue(); - - if ( ce.isDorecreate() ) { - session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() ); - actionQueue.addAction( - new CollectionRecreateAction( - coll, - ce.getCurrentPersister(), - ce.getCurrentKey(), - session - ) - ); - } - if ( ce.isDoremove() ) { - session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() ); - actionQueue.addAction( - new CollectionRemoveAction( - coll, - ce.getLoadedPersister(), - ce.getLoadedKey(), - ce.isSnapshotEmpty(coll), - session - ) - ); - } - if ( ce.isDoupdate() ) { - session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() ); - actionQueue.addAction( - new CollectionUpdateAction( - coll, - ce.getLoadedPersister(), - ce.getLoadedKey(), - ce.isSnapshotEmpty(coll), - session - ) - ); - } - - // todo : I'm not sure the !wasInitialized part should really be part of this check - if ( !coll.wasInitialized() && coll.hasQueuedOperations() ) { - actionQueue.addAction( - new QueuedOperationCollectionAction( - coll, - ce.getLoadedPersister(), - ce.getLoadedKey(), - session - ) - ); - } - - } + final ActionQueue actionQueue = session.getActionQueue(); + final Interceptor interceptor = session.getInterceptor(); + persistenceContext.forEachCollectionEntry( + (coll, ce) -> { + if ( ce.isDorecreate() ) { + interceptor.onCollectionRecreate( coll, ce.getCurrentKey() ); + actionQueue.addAction( + new CollectionRecreateAction( + coll, + ce.getCurrentPersister(), + ce.getCurrentKey(), + session + ) + ); + } + if ( ce.isDoremove() ) { + interceptor.onCollectionRemove( coll, ce.getLoadedKey() ); + actionQueue.addAction( + new CollectionRemoveAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + ce.isSnapshotEmpty( coll ), + session + ) + ); + } + if ( ce.isDoupdate() ) { + interceptor.onCollectionUpdate( coll, ce.getLoadedKey() ); + actionQueue.addAction( + new CollectionUpdateAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + ce.isSnapshotEmpty( coll ), + session + ) + ); + } + // todo : I'm not sure the !wasInitialized part should really be part of this check + if ( !coll.wasInitialized() && coll.hasQueuedOperations() ) { + actionQueue.addAction( + new QueuedOperationCollectionAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + session + ) + ); + } + }, true ); actionQueue.sortCollectionActions(); @@ -387,27 +375,25 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi // the batch fetching queues should also be cleared - especially the collection batch fetching one persistenceContext.getBatchFetchQueue().clear(); - for ( Map.Entry me : IdentityMap.concurrentEntries( persistenceContext.getCollectionEntries() ) ) { - CollectionEntry collectionEntry = me.getValue(); - PersistentCollection persistentCollection = me.getKey(); - collectionEntry.postFlush(persistentCollection); - if ( collectionEntry.getLoadedPersister() == null ) { - //if the collection is dereferenced, unset its session reference and remove from the session cache - //iter.remove(); //does not work, since the entrySet is not backed by the set - persistentCollection.unsetSession( session ); - persistenceContext.getCollectionEntries() - .remove(persistentCollection); - } - else { - //otherwise recreate the mapping between the collection and its key - CollectionKey collectionKey = new CollectionKey( - collectionEntry.getLoadedPersister(), - collectionEntry.getLoadedKey() - ); - persistenceContext.getCollectionsByKey().put(collectionKey, persistentCollection); - } - } - + persistenceContext.forEachCollectionEntry( + (persistentCollection, collectionEntry) -> { + collectionEntry.postFlush( persistentCollection ); + if ( collectionEntry.getLoadedPersister() == null ) { + //if the collection is dereferenced, unset its session reference and remove from the session cache + //iter.remove(); //does not work, since the entrySet is not backed by the set + persistentCollection.unsetSession( session ); + persistenceContext.removeCollectionEntry( persistentCollection ); + } + else { + //otherwise recreate the mapping between the collection and its key + CollectionKey collectionKey = new CollectionKey( + collectionEntry.getLoadedPersister(), + collectionEntry.getLoadedKey() + ); + persistenceContext.getCollectionsByKey().put( collectionKey, persistentCollection ); + } + }, true + ); } protected void postPostFlush(SessionImplementor session) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EvictVisitor.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EvictVisitor.java index d47d5498e6..fa7793b1eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EvictVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EvictVisitor.java @@ -67,7 +67,7 @@ public class EvictVisitor extends AbstractVisitor { private void evictCollection(PersistentCollection collection) { final PersistenceContext persistenceContext = getSession().getPersistenceContextInternal(); - CollectionEntry ce = (CollectionEntry) persistenceContext.getCollectionEntries().remove(collection); + CollectionEntry ce = persistenceContext.removeCollectionEntry( collection ); if ( LOG.isDebugEnabled() ) { LOG.debugf( "Evicting collection: %s", diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/IdentityMap.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/IdentityMap.java index eeda7b1ab1..d64786bbaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/IdentityMap.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/IdentityMap.java @@ -13,6 +13,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -62,6 +63,15 @@ public final class IdentityMap implements Map { identityMap.map.forEach( (kIdentityKey, v) -> consumer.accept( kIdentityKey.key ) ); } + /** + * Override Map{@link #forEach(BiConsumer)} to provide a more efficient implementation + * @param action the operation to apply to each element + */ + @Override + public void forEach(BiConsumer action) { + map.forEach( (k,v) -> action.accept( k.key, v ) ); + } + public Iterator keyIterator() { return new KeyIterator( map.keySet().iterator() ); } From dbbc24c2e16bc99606aaf7e17fc3c1b8cabf31c5 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 21:26:42 +0100 Subject: [PATCH 37/99] HHH-13587 Make StatefulPersistenceContext#nonlazyCollections a lazily initialized field --- .../engine/internal/StatefulPersistenceContext.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index b072382e11..f9663713cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -163,8 +163,6 @@ public class StatefulPersistenceContext implements PersistenceContext { entityEntryContext = new EntityEntryContext( this ); collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); - - nonlazyCollections = new ArrayList<>( INIT_COLL_SIZE ); } private ConcurrentMap getOrInitializeProxiesByKey() { @@ -252,6 +250,7 @@ public class StatefulPersistenceContext implements PersistenceContext { parentsByChild = null; entitySnapshotsByKey.clear(); collectionsByKey.clear(); + nonlazyCollections = null; collectionEntries = null; unownedCollections = null; proxiesByKey = null; @@ -952,6 +951,9 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void addNonLazyCollection(PersistentCollection collection) { + if ( nonlazyCollections == null ) { + nonlazyCollections = new ArrayList<>( INIT_COLL_SIZE ); + } nonlazyCollections.add( collection ); } @@ -965,7 +967,7 @@ public class StatefulPersistenceContext implements PersistenceContext { loadCounter++; try { int size; - while ( ( size = nonlazyCollections.size() ) > 0 ) { + while ( nonlazyCollections != null && ( size = nonlazyCollections.size() ) > 0 ) { //note that each iteration of the loop may add new elements nonlazyCollections.remove( size - 1 ).forceInitialization(); } From 05b888e0c0a66522ea921a808a801a3f45172b5b Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 21:59:07 +0100 Subject: [PATCH 38/99] HHH-13587 Make StatefulPersistenceContext#arrayHolders lazily initialized as well --- .../internal/StatefulPersistenceContext.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index f9663713cc..0509877999 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -162,7 +162,6 @@ public class StatefulPersistenceContext implements PersistenceContext { entityEntryContext = new EntityEntryContext( this ); collectionsByKey = new HashMap<>( INIT_COLL_SIZE ); - arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); } private ConcurrentMap getOrInitializeProxiesByKey() { @@ -243,7 +242,7 @@ public class StatefulPersistenceContext implements PersistenceContext { IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) ); } - arrayHolders.clear(); + arrayHolders = null; entitiesByKey.clear(); entitiesByUniqueKey.clear(); entityEntryContext.clear(); @@ -981,18 +980,21 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public PersistentCollection getCollectionHolder(Object array) { - return arrayHolders.get( array ); + return arrayHolders == null ? null : arrayHolders.get( array ); } @Override public void addCollectionHolder(PersistentCollection holder) { //TODO:refactor + make this method private + if ( arrayHolders == null ) { + arrayHolders = new IdentityHashMap<>( INIT_COLL_SIZE ); + } arrayHolders.put( holder.getValue(), holder ); } @Override public PersistentCollection removeCollectionHolder(Object array) { - return arrayHolders.remove( array ); + return arrayHolders != null ? arrayHolders.remove( array ) : null; } @Override @@ -1583,13 +1585,18 @@ public class StatefulPersistenceContext implements PersistenceContext { } } - oos.writeInt( arrayHolders.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + arrayHolders.size() + "] arrayHolders entries" ); + if ( arrayHolders == null ) { + oos.writeInt( 0 ); } - for ( Map.Entry entry : arrayHolders.entrySet() ) { - oos.writeObject( entry.getKey() ); - oos.writeObject( entry.getValue() ); + else { + oos.writeInt( arrayHolders.size() ); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Starting serialization of [" + arrayHolders.size() + "] arrayHolders entries" ); + } + for ( Map.Entry entry : arrayHolders.entrySet() ) { + oos.writeObject( entry.getKey() ); + oos.writeObject( entry.getValue() ); + } } if ( nullifiableEntityKeys == null ) { @@ -1707,9 +1714,11 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( LOG.isTraceEnabled() ) { LOG.trace( "Starting deserialization of [" + count + "] arrayHolders entries" ); } - rtn.arrayHolders = new IdentityHashMap<>( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count ); - for ( int i = 0; i < count; i++ ) { - rtn.arrayHolders.put( ois.readObject(), (PersistentCollection) ois.readObject() ); + if ( count != 0 ) { + rtn.arrayHolders = new IdentityHashMap<>( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count ); + for ( int i = 0; i < count; i++ ) { + rtn.arrayHolders.put( ois.readObject(), (PersistentCollection) ois.readObject() ); + } } count = ois.readInt(); From 7531ed793a9ab08c18f10491af293274f018d691 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 21:50:58 +0100 Subject: [PATCH 39/99] HHH-13587 Make StatefulPersistenceContext#entitiesByUniqueKey lazily initialized as well --- .../internal/StatefulPersistenceContext.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 0509877999..dabcc510c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -13,6 +13,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -157,7 +158,6 @@ public class StatefulPersistenceContext implements PersistenceContext { this.session = session; entitiesByKey = new HashMap<>( INIT_COLL_SIZE ); - entitiesByUniqueKey = new HashMap<>( INIT_COLL_SIZE ); entitySnapshotsByKey = new HashMap<>( INIT_COLL_SIZE ); entityEntryContext = new EntityEntryContext( this ); @@ -244,7 +244,7 @@ public class StatefulPersistenceContext implements PersistenceContext { arrayHolders = null; entitiesByKey.clear(); - entitiesByUniqueKey.clear(); + entitiesByUniqueKey = null; entityEntryContext.clear(); parentsByChild = null; entitySnapshotsByKey.clear(); @@ -406,12 +406,15 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Object removeEntity(EntityKey key) { final Object entity = entitiesByKey.remove( key ); - final Iterator itr = entitiesByUniqueKey.values().iterator(); - while ( itr.hasNext() ) { - if ( itr.next() == entity ) { - itr.remove(); + if ( entitiesByUniqueKey != null ) { + final Iterator itr = entitiesByUniqueKey.values().iterator(); + while ( itr.hasNext() ) { + if ( itr.next() == entity ) { + itr.remove(); + } } } + // Clear all parent cache parentsByChild = null; entitySnapshotsByKey.remove( key ); @@ -427,11 +430,14 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public Object getEntity(EntityUniqueKey euk) { - return entitiesByUniqueKey.get( euk ); + return entitiesByUniqueKey == null ? null : entitiesByUniqueKey.get( euk ); } @Override public void addEntity(EntityUniqueKey euk, Object entity) { + if ( entitiesByUniqueKey == null ) { + entitiesByUniqueKey = new HashMap<>( INIT_COLL_SIZE ); + } entitiesByUniqueKey.put( euk, entity ); } @@ -1528,13 +1534,18 @@ public class StatefulPersistenceContext implements PersistenceContext { oos.writeObject( entry.getValue() ); } - oos.writeInt( entitiesByUniqueKey.size() ); - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Starting serialization of [" + entitiesByUniqueKey.size() + "] entitiesByUniqueKey entries" ); + if ( entitiesByUniqueKey == null ) { + oos.writeInt( 0 ); } - for ( Map.Entry entry : entitiesByUniqueKey.entrySet() ) { - entry.getKey().serialize( oos ); - oos.writeObject( entry.getValue() ); + else { + oos.writeInt( entitiesByUniqueKey.size() ); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Starting serialization of [" + entitiesByUniqueKey.size() + "] entitiesByUniqueKey entries" ); + } + for ( Map.Entry entry : entitiesByUniqueKey.entrySet() ) { + entry.getKey().serialize( oos ); + oos.writeObject( entry.getValue() ); + } } if ( proxiesByKey == null ) { @@ -1655,9 +1666,11 @@ public class StatefulPersistenceContext implements PersistenceContext { if ( LOG.isTraceEnabled() ) { LOG.trace( "Starting deserialization of [" + count + "] entitiesByUniqueKey entries" ); } - rtn.entitiesByUniqueKey = new HashMap<>( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count ); - for ( int i = 0; i < count; i++ ) { - rtn.entitiesByUniqueKey.put( EntityUniqueKey.deserialize( ois, session ), ois.readObject() ); + if ( count != 0 ) { + rtn.entitiesByUniqueKey = new HashMap<>( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count ); + for ( int i = 0; i < count; i++ ) { + rtn.entitiesByUniqueKey.put( EntityUniqueKey.deserialize( ois, session ), ois.readObject() ); + } } count = ois.readInt(); From da847f4b574c3b21eb8ddd8d12b77011e2f63a53 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 17 Aug 2019 22:43:04 +0100 Subject: [PATCH 40/99] HHH-13587 Avoid using deprecated PersistenceContext#getCollectionEntries in tests --- .../MultipleSessionCollectionWarningTest.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionWarningTest.java b/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionWarningTest.java index 9cd6db69f8..e7a0090c74 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionWarningTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionWarningTest.java @@ -41,6 +41,7 @@ import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.collection.internal.AbstractPersistentCollection; +import org.hibernate.collection.internal.PersistentSet; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.SessionImplementor; @@ -81,9 +82,8 @@ public class MultipleSessionCollectionWarningTest extends BaseCoreFunctionalTest // gets logged. s1 will not function properly so the transaction will ultimately need // to be rolled-back. - CollectionEntry ce = (CollectionEntry) ( (SessionImplementor) s1 ).getPersistenceContext() - .getCollectionEntries() - .remove( p.children ); + CollectionEntry ce = ( (SessionImplementor) s1 ).getPersistenceContext() + .removeCollectionEntry( (PersistentSet) p.children ); assertNotNull( ce ); // the collection session should still be s1; the collection is no longer "connected" because its @@ -132,9 +132,8 @@ public class MultipleSessionCollectionWarningTest extends BaseCoreFunctionalTest // gets logged. s1 will not function properly so the transaction will ultimately need // to be rolled-back. - CollectionEntry ce = (CollectionEntry) ( (SessionImplementor) s1 ).getPersistenceContext() - .getCollectionEntries() - .remove( p.children ); + CollectionEntry ce = ( (SessionImplementor) s1 ).getPersistenceContext() + .removeCollectionEntry( (PersistentSet) p.children ); assertNotNull( ce ); // the collection session should still be s1; the collection is no longer "connected" because its @@ -180,9 +179,8 @@ public class MultipleSessionCollectionWarningTest extends BaseCoreFunctionalTest // gets logged. s1 will not function properly so the transaction will ultimately need // to be rolled-back. - CollectionEntry ce = (CollectionEntry) ( (SessionImplementor) s1 ).getPersistenceContext() - .getCollectionEntries() - .remove( p.children ); + CollectionEntry ce = ( (SessionImplementor) s1 ).getPersistenceContext() + .removeCollectionEntry( (PersistentSet) p.children ); assertNotNull( ce ); // the collection session should still be s1; the collection is no longer "connected" because its From e0f40474296c44cc8ec5e36256d3e0ddf118c7b8 Mon Sep 17 00:00:00 2001 From: Ladislav Kulhanek Date: Thu, 11 Oct 2018 15:49:44 +0200 Subject: [PATCH 41/99] HHH-12993 Omit joining of superclass table when querying subclass only --- .../SessionFactoryOptionsBuilder.java | 9 + ...stractDelegatingSessionFactoryOptions.java | 5 + .../boot/spi/SessionFactoryOptions.java | 2 + .../org/hibernate/cfg/AvailableSettings.java | 15 + .../engine/internal/JoinSequence.java | 12 +- .../ast/tree/AbstractMapComponentNode.java | 17 ++ .../hql/internal/ast/tree/DotNode.java | 36 +++ .../hql/internal/ast/tree/FromElement.java | 4 + .../internal/ast/tree/FromElementType.java | 10 + .../internal/ast/tree/FromReferenceNode.java | 10 + .../hql/internal/ast/util/ASTPrinter.java | 44 ++- .../ast/util/ASTReferencedTablesPrinter.java | 72 +++++ .../hql/internal/ast/util/JoinProcessor.java | 87 ++++++ .../hql/internal/ast/util/TokenPrinters.java | 2 + .../entity/AbstractEntityPersister.java | 71 ++++- .../hibernate/persister/entity/Joinable.java | 13 + .../entity/JoinedSubclassEntityPersister.java | 6 + .../entity/SingleTableEntityPersister.java | 1 + .../entity/UnionSubclassEntityPersister.java | 2 + .../java/org/hibernate/test/hql/HQLTest.java | 143 +++++++--- .../OmitAncestorJoinTest.java | 167 +++++++++++ ...inWhenCommonSecondaryTablePresentTest.java | 262 ++++++++++++++++++ ...storJoinWhenSecondaryTablePresentTest.java | 107 +++++++ .../OmitAncestorTestCase.java | 48 ++++ .../test/joinwithoutancestor/SqlAsserts.java | 140 ++++++++++ 25 files changed, 1224 insertions(+), 61 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTReferencedTablesPrinter.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenCommonSecondaryTablePresentTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenSecondaryTablePresentTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorTestCase.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/SqlAsserts.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index 789d01c8a5..52cf2f8ce5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -99,6 +99,7 @@ import static org.hibernate.cfg.AvailableSettings.LOG_SESSION_METRICS; import static org.hibernate.cfg.AvailableSettings.MAX_FETCH_DEPTH; import static org.hibernate.cfg.AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER; import static org.hibernate.cfg.AvailableSettings.NATIVE_EXCEPTION_HANDLING_51_COMPLIANCE; +import static org.hibernate.cfg.AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES; import static org.hibernate.cfg.AvailableSettings.ORDER_INSERTS; import static org.hibernate.cfg.AvailableSettings.JPA_CALLBACKS_ENABLED; import static org.hibernate.cfg.AvailableSettings.ORDER_UPDATES; @@ -212,6 +213,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private final boolean procedureParameterNullPassingEnabled; private final boolean collectionJoinSubqueryRewriteEnabled; private boolean jdbcStyleParamsZeroBased; + private final boolean omitJoinOfSuperclassTablesEnabled; // Caching private boolean secondLevelCacheEnabled; @@ -360,6 +362,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { CONVENTIONAL_JAVA_CONSTANTS, BOOLEAN, true ); this.procedureParameterNullPassingEnabled = cfgService.getSetting( PROCEDURE_NULL_PARAM_PASSING, BOOLEAN, false ); this.collectionJoinSubqueryRewriteEnabled = cfgService.getSetting( COLLECTION_JOIN_SUBQUERY, BOOLEAN, true ); + this.omitJoinOfSuperclassTablesEnabled = cfgService.getSetting( OMIT_JOIN_OF_SUPERCLASS_TABLES, BOOLEAN, true ); final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class ); if ( !NoCachingRegionFactory.class.isInstance( regionFactory ) ) { @@ -1064,6 +1067,12 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { return enhancementAsProxyEnabled; } + @Override + public boolean isOmitJoinOfSuperclassTablesEnabled() { + return omitJoinOfSuperclassTablesEnabled; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // In-flight mutation access diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 1df0a779ae..a3de3a7963 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -447,4 +447,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp public boolean isEnhancementAsProxyEnabled() { return delegate.isEnhancementAsProxyEnabled(); } + + @Override + public boolean isOmitJoinOfSuperclassTablesEnabled() { + return delegate.isOmitJoinOfSuperclassTablesEnabled(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index 044ab8c94c..be055237b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -314,4 +314,6 @@ public interface SessionFactoryOptions { default boolean isEnhancementAsProxyEnabled() { return false; } + + boolean isOmitJoinOfSuperclassTablesEnabled(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 21852897e8..140ee68ed0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -2060,4 +2060,19 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings { * @since 5.4 */ String SEQUENCE_INCREMENT_SIZE_MISMATCH_STRATEGY = "hibernate.id.sequence.increment_size_mismatch_strategy"; + + /** + *

+ * When you use {@link javax.persistence.InheritanceType#JOINED} strategy for inheritance mapping and query + * a value from an entity, all superclass tables are joined in the query regardless you need them. With + * this setting set to true only superclass tables which are really needed are joined. + *

+ *

+ * The default value is true. + *

+ * + * @since 5.4 + */ + String OMIT_JOIN_OF_SUPERCLASS_TABLES = "hibernate.query.omit_join_of_superclass_tables"; + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinSequence.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinSequence.java index bf2d699ea8..83c6d1cbdd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinSequence.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/JoinSequence.java @@ -47,6 +47,7 @@ public class JoinSequence { private Selector selector; private JoinSequence next; private boolean isFromPart; + private Set queryReferencedTables; /** * Constructs a JoinSequence @@ -466,7 +467,7 @@ public class JoinSequence { Set treatAsDeclarations) { final boolean include = includeSubclassJoins && isIncluded( alias ); joinFragment.addJoins( - joinable.fromJoinFragment( alias, innerJoin, include, treatAsDeclarations ), + joinable.fromJoinFragment( alias, innerJoin, include, treatAsDeclarations, queryReferencedTables ), joinable.whereJoinFragment( alias, innerJoin, include, treatAsDeclarations ) ); } @@ -573,6 +574,15 @@ public class JoinSequence { return useThetaStyle; } + /** + * Set all tables the query refers to. It allows to optimize the query. + * + * @param queryReferencedTables + */ + public void setQueryReferencedTables(Set queryReferencedTables) { + this.queryReferencedTables = queryReferencedTables; + } + public Join getFirstJoin() { return joins.get( 0 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java index 1daa679fc2..48ee9f35e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/AbstractMapComponentNode.java @@ -11,6 +11,8 @@ import java.util.Map; import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; import org.hibernate.hql.internal.ast.util.ColumnHelper; import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.CollectionType; import org.hibernate.type.Type; @@ -123,4 +125,19 @@ public abstract class AbstractMapComponentNode extends FromReferenceNode impleme return MapKeyEntityFromElement.buildKeyJoin( getFromElement() ); } + + @Override + public String[] getReferencedTables() { + String[] referencedTables = null; + FromElement fromElement = getFromElement(); + if ( fromElement != null ) { + EntityPersister entityPersister = fromElement.getEntityPersister(); + if ( entityPersister != null && entityPersister instanceof AbstractEntityPersister ) { + AbstractEntityPersister abstractEntityPersister = (AbstractEntityPersister) entityPersister; + referencedTables = abstractEntityPersister.getTableNames(); + } + } + return referencedTables; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java index 77c23aaa42..7b70a52b9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java @@ -76,10 +76,18 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec * The identifier that is the name of the property. */ private String propertyName; + + /** + * The identifier that is the name of the property. In comparison with {@link #propertyName} + * it is always identical with identifier in the query, it is not changed during processing. + */ + private String originalPropertyName; + /** * The full path, to the root alias of this dot node. */ private String path; + /** * The unresolved property path relative to this dot node. */ @@ -160,6 +168,7 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec // Set the attributes of the property reference expression. String propName = property.getText(); propertyName = propName; + originalPropertyName = propName; // If the uresolved property path isn't set yet, just use the property name. if ( propertyPath == null ) { propertyPath = propName; @@ -692,6 +701,25 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec return super.getDataType(); } + @Override + public String[] getReferencedTables() { + String[] referencedTables = null; + AST firstChild = getFirstChild(); + if ( firstChild != null ) { + if ( firstChild instanceof FromReferenceNode ) { + FromReferenceNode fromReferenceNode = (FromReferenceNode) firstChild; + FromElement fromElement = fromReferenceNode.getFromElement(); + if ( fromElement != null ) { + String table = fromElement.getPropertyTableName( getOriginalPropertyName() ); + if ( table != null ) { + referencedTables = new String[] { table }; + } + } + } + } + return referencedTables; + } + public void setPropertyPath(String propertyPath) { this.propertyPath = propertyPath; } @@ -700,6 +728,14 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec return propertyPath; } + public String getPropertyName() { + return propertyName; + } + + public String getOriginalPropertyName() { + return originalPropertyName; + } + public FromReferenceNode getLhs() { FromReferenceNode lhs = ( (FromReferenceNode) getFirstChild() ); if ( lhs == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index 8484c999e6..960a7d073e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -517,6 +517,10 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa return elementType.getPropertyType( propertyName, propertyPath ); } + public String getPropertyTableName(String propertyName) { + return elementType.getPropertyTableName( propertyName ); + } + public String[] toColumns(String tableAlias, String path, boolean inSelect) { return elementType.toColumns( tableAlias, path, inSelect ); } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java index 9fe8f365e4..665733981e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementType.java @@ -27,6 +27,7 @@ import org.hibernate.param.ParameterSpecification; import org.hibernate.persister.collection.CollectionPropertyMapping; import org.hibernate.persister.collection.CollectionPropertyNames; import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.PropertyMapping; @@ -372,6 +373,15 @@ class FromElementType { return queryableCollection; } + public String getPropertyTableName(String propertyName) { + checkInitialized(); + if ( this.persister != null ) { + AbstractEntityPersister aep = (AbstractEntityPersister) this.persister; + return aep.getPropertyTableName( propertyName ); + } + return null; + } + /** * Returns the type of a property, given it's name (the last part) and the full path. * diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java index 7e44fabe97..8446af2aee 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromReferenceNode.java @@ -137,4 +137,14 @@ public abstract class FromReferenceNode extends AbstractSelectExpression || getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE; } + /** + * Returns table names which are referenced by this node. If the tables + * can not be determined it returns null. + * + * @return table names or null. + */ + public String[] getReferencedTables() { + return null; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTPrinter.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTPrinter.java index a9ed6538ca..43408a478f 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTPrinter.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTPrinter.java @@ -10,6 +10,8 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.hibernate.hql.internal.ast.tree.DisplayableNode; @@ -25,7 +27,7 @@ import antlr.collections.AST; * @author Joshua Davis * @author Steve Ebersole */ -public final class ASTPrinter { +public class ASTPrinter { // This is a map: array index is the ANTLR Token ID, array value is the name of that token. // There might be gaps in the array (null values) but it's generally quite compact. @@ -103,15 +105,7 @@ public final class ASTPrinter { return; } - for ( AST parent : parents ) { - if ( parent.getNextSibling() == null ) { - - pw.print( " " ); - } - else { - pw.print( " | " ); - } - } + indentLine( parents, pw ); if ( ast.getNextSibling() == null ) { pw.print( " \\-" ); @@ -121,6 +115,7 @@ public final class ASTPrinter { } showNode( pw, ast ); + showNodeProperties( parents, pw, ast ); ArrayList newParents = new ArrayList( parents ); newParents.add( ast ); @@ -130,6 +125,17 @@ public final class ASTPrinter { newParents.clear(); } + private void indentLine(List parents, PrintWriter pw) { + for ( AST parent : parents ) { + if ( parent.getNextSibling() == null ) { + pw.print( " " ); + } + else { + pw.print( " | " ); + } + } + } + private void showNode(PrintWriter pw, AST ast) { String s = nodeToString( ast ); pw.println( s ); @@ -158,6 +164,24 @@ public final class ASTPrinter { return buf.toString(); } + private void showNodeProperties(ArrayList parents, PrintWriter pw, AST ast) { + Map nodeProperties = createNodeProperties( ast ); + ArrayList parentsAndNode = new ArrayList<>( parents ); + parentsAndNode.add( ast ); + for ( String propertyName : nodeProperties.keySet() ) { + indentLine( parentsAndNode, pw ); + pw.println( propertyToString( propertyName, nodeProperties.get( propertyName ), ast ) ); + } + } + + public LinkedHashMap createNodeProperties(AST ast) { + return new LinkedHashMap<>(); + } + + public String propertyToString(String label, Object value, AST ast) { + return String.format( "%s: %s", label, value ); + } + public static void appendEscapedMultibyteChars(String text, StringBuilder buf) { char[] chars = text.toCharArray(); for ( char aChar : chars ) { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTReferencedTablesPrinter.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTReferencedTablesPrinter.java new file mode 100644 index 0000000000..1999881f1d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/ASTReferencedTablesPrinter.java @@ -0,0 +1,72 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.hql.internal.ast.util; + +import java.util.Arrays; +import java.util.LinkedHashMap; + +import org.hibernate.hql.internal.ast.tree.DotNode; +import org.hibernate.hql.internal.ast.tree.FromElement; +import org.hibernate.hql.internal.ast.tree.FromReferenceNode; +import org.hibernate.hql.internal.ast.tree.IdentNode; +import org.hibernate.hql.internal.ast.tree.SelectClause; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; + +import antlr.collections.AST; + +public class ASTReferencedTablesPrinter extends ASTPrinter { + + public ASTReferencedTablesPrinter(Class tokenTypeConstants) { + super( tokenTypeConstants ); + } + + @Override + public String nodeToString(AST ast) { + if ( ast == null ) { + return "{node:null}"; + } + return ast.getClass().getSimpleName(); + } + + @Override + public LinkedHashMap createNodeProperties(AST node) { + LinkedHashMap props = new LinkedHashMap<>(); + if ( node instanceof FromReferenceNode ) { + FromReferenceNode frn = (FromReferenceNode) node; + FromElement fromElement = frn.getFromElement(); + EntityPersister entityPersister = fromElement != null ? fromElement.getEntityPersister() : null; + String entityPersisterStr = entityPersister != null ? entityPersister.toString() : null; + props.put( "persister", entityPersisterStr ); + String referencedTablesStr = Arrays.toString( frn.getReferencedTables() ); + props.put( "referencedTables", referencedTablesStr ); + } + if ( node instanceof DotNode ) { + DotNode dn = (DotNode) node; + props.put( "path", dn.getPath() ); + props.put( "originalPropertyName", dn.getOriginalPropertyName() ); + } + if ( node instanceof IdentNode ) { + IdentNode in = (IdentNode) node; + props.put( "originalText", in.getOriginalText() ); + } + if ( node instanceof SelectClause ) { + SelectClause sc = (SelectClause) node; + for ( Object element : sc.getFromElementsForLoad() ) { + FromElement fromElement = (FromElement) element; + EntityPersister entityPersister = fromElement.getEntityPersister(); + if ( entityPersister != null && entityPersister instanceof AbstractEntityPersister ) { + AbstractEntityPersister aep = (AbstractEntityPersister) entityPersister; + String entityClass = aep.getMappedClass().getSimpleName(); + String tables = Arrays.toString( aep.getTableNames() ); + props.put( String.format( "referencedTables(entity %s)", entityClass ), tables ); + } + } + } + return props; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java index 0196149407..f3361a8e5e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java @@ -8,14 +8,17 @@ package org.hibernate.hql.internal.ast.util; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hibernate.AssertionFailure; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.internal.JoinSequence; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -24,6 +27,7 @@ import org.hibernate.hql.internal.ast.HqlSqlWalker; import org.hibernate.hql.internal.ast.tree.DotNode; import org.hibernate.hql.internal.ast.tree.FromClause; import org.hibernate.hql.internal.ast.tree.FromElement; +import org.hibernate.hql.internal.ast.tree.FromReferenceNode; import org.hibernate.hql.internal.ast.tree.ImpliedFromElement; import org.hibernate.hql.internal.ast.tree.ParameterContainer; import org.hibernate.hql.internal.ast.tree.QueryNode; @@ -33,11 +37,16 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FilterImpl; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.param.DynamicFilterParameterSpecification; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinType; import org.hibernate.type.Type; +import antlr.collections.AST; + /** * Performs the post-processing of the join information gathered during semantic analysis. * The join generating classes are complex, this encapsulates some of the JoinSequence-related @@ -94,9 +103,86 @@ public class JoinProcessor implements SqlTokenTypes { } } + private List findAllNodes(AST node, Class clazz) { + ArrayList found = new ArrayList<>(); + doFindAllNodes( node, clazz, found ); + return found; + } + + private void doFindAllNodes(AST node, Class clazz, List found) { + if ( clazz.isAssignableFrom( node.getClass() ) ) { + found.add( (T) node ); + } + if ( node.getFirstChild() != null ) { + doFindAllNodes( node.getFirstChild(), clazz, found ); + } + if ( node.getNextSibling() != null ) { + doFindAllNodes( node.getNextSibling(), clazz, found ); + } + } + + private Set findQueryReferencedTables(QueryNode query) { + if ( !walker.getSessionFactoryHelper() + .getFactory() + .getSessionFactoryOptions() + .isOmitJoinOfSuperclassTablesEnabled() ) { + if ( LOG.isDebugEnabled() ) { + LOG.debug( String.format( + "Finding of query referenced tables is skipped because the feature is disabled. See %s", + AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES + ) ); + } + return null; + } + + if ( CollectionHelper.isNotEmpty( walker.getEnabledFilters() ) ) { + LOG.debug( "Finding of query referenced tables is skipped because filters are enabled." ); + return null; + } + + if ( LOG.isDebugEnabled() ) { + LOG.debug( TokenPrinters.REFERENCED_TABLES_PRINTER.showAsString( + query, + "Tables referenced from query nodes:" + ) ); + } + + Set result = new HashSet<>(); + + // Find tables referenced by FromReferenceNodes + List fromReferenceNodes = findAllNodes( query, FromReferenceNode.class ); + for ( FromReferenceNode node : fromReferenceNodes ) { + String[] tables = node.getReferencedTables(); + if ( tables != null ) { + for ( String table : tables ) { + result.add( table ); + } + } + } + + // Find tables referenced by fromElementsForLoad + if ( query.getSelectClause() != null ) { + for ( Object element : query.getSelectClause().getFromElementsForLoad() ) { + FromElement fromElement = (FromElement) element; + EntityPersister entityPersister = fromElement.getEntityPersister(); + if ( entityPersister != null && entityPersister instanceof AbstractEntityPersister ) { + AbstractEntityPersister aep = (AbstractEntityPersister) entityPersister; + String[] tables = aep.getTableNames(); + for ( String table : tables ) { + result.add( table ); + } + } + } + } + + return result; + } + public void processJoins(QueryNode query) { final FromClause fromClause = query.getFromClause(); + Set queryReferencedTables = findQueryReferencedTables( query ); + final List fromElements; if ( DotNode.useThetaStyleImplicitJoins ) { // for regression testing against output from the old parser... @@ -136,6 +222,7 @@ public class JoinProcessor implements SqlTokenTypes { while ( iter.hasNext() ) { final FromElement fromElement = (FromElement) iter.next(); JoinSequence join = fromElement.getJoinSequence(); + join.setQueryReferencedTables( queryReferencedTables ); join.setSelector( new JoinSequence.Selector() { public boolean includeSubclasses(String alias) { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/TokenPrinters.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/TokenPrinters.java index 66a03adb3b..be77713515 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/TokenPrinters.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/TokenPrinters.java @@ -21,4 +21,6 @@ public interface TokenPrinters { ASTPrinter ORDERBY_FRAGMENT_PRINTER = new ASTPrinter( GeneratedOrderByFragmentRendererTokenTypes.class ); + ASTPrinter REFERENCED_TABLES_PRINTER = new ASTReferencedTablesPrinter( SqlTokenTypes.class ); + } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 8f41e64aa8..9bd6ebb333 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -533,6 +533,14 @@ public abstract class AbstractEntityPersister return propertySelectable; } + public String[] getTableNames() { + String[] tableNames = new String[getTableSpan()]; + for ( int i = 0; i < tableNames.length; i++ ) { + tableNames[i] = getTableName( i ); + } + return tableNames; + } + @SuppressWarnings("UnnecessaryBoxing") public AbstractEntityPersister( final PersistentClass persistentClass, @@ -3890,7 +3898,8 @@ public abstract class AbstractEntityPersister alias, innerJoin, includeSubclasses, - Collections.emptySet() + Collections.emptySet(), + null ).toFromFragmentString(); } @@ -3903,7 +3912,19 @@ public abstract class AbstractEntityPersister // NOTE : Not calling createJoin here is just a performance optimization return getSubclassTableSpan() == 1 ? "" - : createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toFromFragmentString(); + : createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations, null ).toFromFragmentString(); + } + + @Override + public String fromJoinFragment( + String alias, + boolean innerJoin, + boolean includeSubclasses, + Set treatAsDeclarations, + Set referencedTables) { + return getSubclassTableSpan() == 1 + ? "" + : createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations, referencedTables ).toFromFragmentString(); } @Override @@ -3915,7 +3936,8 @@ public abstract class AbstractEntityPersister alias, innerJoin, includeSubclasses, - Collections.emptySet() + Collections.emptySet(), + null ).toWhereFragmentString(); } @@ -3928,7 +3950,7 @@ public abstract class AbstractEntityPersister // NOTE : Not calling createJoin here is just a performance optimization return getSubclassTableSpan() == 1 ? "" - : createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toWhereFragmentString(); + : createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations, null ).toWhereFragmentString(); } protected boolean isSubclassTableLazy(int j) { @@ -3940,6 +3962,15 @@ public abstract class AbstractEntityPersister boolean innerJoin, boolean includeSubclasses, Set treatAsDeclarations) { + return createJoin(name, innerJoin, includeSubclasses, treatAsDeclarations, null); + } + + protected JoinFragment createJoin( + String name, + boolean innerJoin, + boolean includeSubclasses, + Set treatAsDeclarations, + Set referencedTables) { // IMPL NOTE : all joins join to the pk of the driving table final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() ); final JoinFragment join = getFactory().getDialect().createOuterJoinFragment(); @@ -3950,7 +3981,8 @@ public abstract class AbstractEntityPersister j, innerJoin, includeSubclasses, - treatAsDeclarations + treatAsDeclarations, + referencedTables ); if ( joinType != null && joinType != JoinType.NONE ) { @@ -3971,8 +4003,28 @@ public abstract class AbstractEntityPersister boolean canInnerJoin, boolean includeSubclasses, Set treatAsDeclarations) { + return determineSubclassTableJoinType( + subclassTableNumber, + canInnerJoin, + includeSubclasses, + treatAsDeclarations, + null + ); + } + + protected JoinType determineSubclassTableJoinType( + int subclassTableNumber, + boolean canInnerJoin, + boolean includeSubclasses, + Set treatAsDeclarations, + Set referencedTables) { if ( isClassOrSuperclassTable( subclassTableNumber ) ) { + String superclassTableName = getSubclassTableName( subclassTableNumber ); + if ( referencedTables != null && canOmitSuperclassTableJoin() && !referencedTables.contains( + superclassTableName ) ) { + return JoinType.NONE; + } final boolean shouldInnerJoin = canInnerJoin && !isInverseTable( subclassTableNumber ) && !isNullableTable( subclassTableNumber ); @@ -5735,6 +5787,15 @@ public abstract class AbstractEntityPersister return ArrayHelper.to2DStringArray( polymorphicJoinColumns ); } + /** + * If true, persister can omit superclass tables during joining if they are not needed in the query. + * + * @return true if the persister can do it + */ + public boolean canOmitSuperclassTableJoin() { + return false; + } + private void prepareEntityIdentifierDefinition() { if ( entityIdentifierDefinition != null ) { return; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/Joinable.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/Joinable.java index 2be118ad2c..aef89f331e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/Joinable.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/Joinable.java @@ -57,6 +57,19 @@ public interface Joinable { */ public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses, Set treatAsDeclarations); + /** + * Get the from clause part of any joins + * (optional operation) + */ + default String fromJoinFragment( + String alias, + boolean innerJoin, + boolean includeSubclasses, + Set treatAsDeclarations, + Set referencedTables) { + return fromJoinFragment( alias, innerJoin, includeSubclasses, treatAsDeclarations ); + } + /** * The columns to join on */ diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 02b2f7b6f5..6930d7ffea 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -1053,6 +1053,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { return subclassNamesBySubclassTable[index]; } + @Override public String getPropertyTableName(String propertyName) { Integer index = getEntityMetamodel().getPropertyIndexOrNull( propertyName ); if ( index == null ) { @@ -1118,4 +1119,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) { return new DynamicFilterAliasGenerator(subclassTableNameClosure, rootAlias); } + + @Override + public boolean canOmitSuperclassTableJoin() { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index c6bd95ada1..2ab666d8c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -809,6 +809,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { return isNullableSubclassTable[j]; } + @Override public String getPropertyTableName(String propertyName) { Integer index = getEntityMetamodel().getPropertyIndexOrNull( propertyName ); if ( index == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 04f72dad9d..090d00a001 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -466,6 +466,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { return true; } + @Override public String getPropertyTableName(String propertyName) { //TODO: check this.... return getTableName(); @@ -483,4 +484,5 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) { return new StaticFilterAliasGenerator( rootAlias ); } + } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/HQLTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/HQLTest.java index 1e84587643..4d256a378f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/HQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/HQLTest.java @@ -16,6 +16,7 @@ import java.util.Map; import org.hibernate.QueryException; import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.H2Dialect; @@ -154,7 +155,7 @@ public class HQLTest extends QueryTranslatorTestCase { assertTranslation( "from Animal a where a.offspring.description = 'xyz'" ); assertTranslation( "from Animal a where a.offspring.father.description = 'xyz'" ); } - + @Test @FailureExpected( jiraKey = "N/A", message = "Lacking ClassicQueryTranslatorFactory support" ) public void testRowValueConstructorSyntaxInInList2() { @@ -209,7 +210,7 @@ public class HQLTest extends QueryTranslatorTestCase { AST inNode = whereNode.getFirstChild(); assertEquals( message, expected, inNode != null && inNode.getType() == HqlTokenTypes.IN ); } - + @Test public void testSubComponentReferences() { assertTranslation( "select c.address.zip.code from ComponentContainer c" ); @@ -227,7 +228,7 @@ public class HQLTest extends QueryTranslatorTestCase { public void testJoinFetchCollectionOfValues() { assertTranslation( "select h from Human as h join fetch h.nickNames" ); } - + @Test public void testCollectionMemberDeclarations2() { assertTranslation( "from Customer c, in(c.orders) o" ); @@ -242,11 +243,13 @@ public class HQLTest extends QueryTranslatorTestCase { // IN asks an alias, but the difference is that the error message from AST // contains the error token location (by lines and columns), which is hardly // to get from Classic query translator --stliu - assertTranslation( "from Customer c, in(c.orders)" ); + assertTranslation( "from Customer c, in(c.orders)" ); } @Test public void testCollectionJoinsInSubselect() { + disableOmittingJoinOfSuperclassTables(); + // caused by some goofiness in FromElementFactory that tries to // handle correlated subqueries (but fails miserably) even though this // is not a correlated subquery. HHH-1248 @@ -340,10 +343,14 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testImplicitJoinsAlongWithCartesianProduct() { - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "select foo.foo from Foo foo, Foo foo2" ); - assertTranslation( "select foo.foo.foo from Foo foo, Foo foo2" ); - DotNode.useThetaStyleImplicitJoins = false; + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("select foo.foo from Foo foo, Foo foo2"); + assertTranslation("select foo.foo.foo from Foo foo, Foo foo2"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -545,18 +552,22 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testCrazyIdFieldNames() { - DotNode.useThetaStyleImplicitJoins = true; - // only regress against non-scalar forms as there appears to be a bug in the classic translator - // in regards to this issue also. Specifically, it interprets the wrong return type, though it gets - // the sql "correct" :/ + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + // only regress against non-scalar forms as there appears to be a bug in the classic translator + // in regards to this issue also. Specifically, it interprets the wrong return type, though it gets + // the sql "correct" :/ - String hql = "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null"; - assertTranslation( hql, new HashMap(), false, null ); + String hql = "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null"; + assertTranslation(hql, new HashMap(), false, null); - hql = "select e.heresAnotherCrazyIdFieldName.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null"; - assertTranslation( hql, new HashMap(), false, null ); + hql = "select e.heresAnotherCrazyIdFieldName.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null"; + assertTranslation(hql, new HashMap(), false, null); - DotNode.useThetaStyleImplicitJoins = false; + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -784,7 +795,7 @@ public class HQLTest extends QueryTranslatorTestCase { || getDialect() instanceof Sybase11Dialect || getDialect() instanceof SybaseASE15Dialect || getDialect() instanceof SybaseAnywhereDialect - || getDialect() instanceof SQLServerDialect + || getDialect() instanceof SQLServerDialect || getDialect() instanceof IngresDialect) { // SybaseASE15Dialect and SybaseAnywhereDialect support '||' // MySQL uses concat(x, y, z) @@ -878,6 +889,8 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testGroupByFunction() { + disableOmittingJoinOfSuperclassTables(); + if ( getDialect() instanceof Oracle8iDialect ) return; // the new hiearchy... if ( getDialect() instanceof PostgreSQLDialect || getDialect() instanceof PostgreSQL81Dialect ) return; if ( getDialect() instanceof TeradataDialect) return; @@ -1027,26 +1040,36 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testImplicitJoinInSelect() { assertTranslation( "select foo, foo.long from Foo foo" ); - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "select foo.foo from Foo foo" ); - assertTranslation( "select foo, foo.foo from Foo foo" ); - assertTranslation( "select foo.foo from Foo foo where foo.foo is not null" ); - DotNode.useThetaStyleImplicitJoins = false; + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("select foo.foo from Foo foo"); + assertTranslation("select foo, foo.foo from Foo foo"); + assertTranslation("select foo.foo from Foo foo where foo.foo is not null"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test public void testSelectExpressions() { - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "select an.mother.mother from Animal an" ); - assertTranslation( "select an.mother.mother.mother from Animal an" ); - assertTranslation( "select an.mother.mother.bodyWeight from Animal an" ); - assertTranslation( "select an.mother.zoo.id from Animal an" ); - assertTranslation( "select user.human.zoo.id from User user" ); - assertTranslation( "select u.userName, u.human.name.first from User u" ); - assertTranslation( "select u.human.name.last, u.human.name.first from User u" ); - assertTranslation( "select bar.baz.name from Bar bar" ); - assertTranslation( "select bar.baz.name, bar.baz.count from Bar bar" ); - DotNode.useThetaStyleImplicitJoins = false; + disableOmittingJoinOfSuperclassTables(); + + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("select an.mother.mother from Animal an"); + assertTranslation("select an.mother.mother.mother from Animal an"); + assertTranslation("select an.mother.mother.bodyWeight from Animal an"); + assertTranslation("select an.mother.zoo.id from Animal an"); + assertTranslation("select user.human.zoo.id from User user"); + assertTranslation("select u.userName, u.human.name.first from User u"); + assertTranslation("select u.human.name.last, u.human.name.first from User u"); + assertTranslation("select bar.baz.name from Bar bar"); + assertTranslation("select bar.baz.name, bar.baz.count from Bar bar"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -1102,10 +1125,14 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testSelectEntityProperty() throws Exception { - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "select an.mother from Animal an" ); - assertTranslation( "select an, an.mother from Animal an" ); - DotNode.useThetaStyleImplicitJoins = false; + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("select an.mother from Animal an"); + assertTranslation("select an, an.mother from Animal an"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -1202,9 +1229,13 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testManyToManyJoinInSubselect() throws Exception { - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "select foo from Foo foo where foo in (select elt from Baz baz join baz.fooArray elt)" ); - DotNode.useThetaStyleImplicitJoins = false; + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("select foo from Foo foo where foo in (select elt from Baz baz join baz.fooArray elt)"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -1258,6 +1289,8 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testSelectDialectFunction() throws Exception { + disableOmittingJoinOfSuperclassTables(); + // From SQLFunctionsTest.testDialectSQLFunctions... if ( getDialect() instanceof HSQLDialect ) { assertTranslation( "select mod(s.count, 2) from org.hibernate.test.legacy.Simple as s where s.id = 10" ); @@ -1358,12 +1391,16 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testOneToOne() throws Exception { + disableOmittingJoinOfSuperclassTables(); + assertTranslation( "from User u where u.human.nickName='Steve'" ); assertTranslation( "from User u where u.human.name.first='Steve'" ); } @Test public void testSelectClauseImplicitJoin() throws Exception { + disableOmittingJoinOfSuperclassTables(); + //assertTranslation( "select d.owner.mother from Dog d" ); //bug in old qt assertTranslation( "select d.owner.mother.description from Dog d" ); //assertTranslation( "select d.owner.mother from Dog d, Dog h" ); @@ -1473,10 +1510,14 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testJoinInSubselect() throws Exception { //new parser uses ANSI-style inner join syntax - DotNode.useThetaStyleImplicitJoins = true; - assertTranslation( "from Animal a where a in (select m from Animal an join an.mother m)" ); - assertTranslation( "from Animal a where a in (select o from Animal an join an.offspring o)" ); - DotNode.useThetaStyleImplicitJoins = false; + boolean originalValue = DotNode.useThetaStyleImplicitJoins; + try { + DotNode.useThetaStyleImplicitJoins = true; + assertTranslation("from Animal a where a in (select m from Animal an join an.mother m)"); + assertTranslation("from Animal a where a in (select o from Animal an join an.offspring o)"); + } finally { + DotNode.useThetaStyleImplicitJoins = originalValue; + } } @Test @@ -1519,12 +1560,16 @@ public class HQLTest extends QueryTranslatorTestCase { @Test public void testManyToManyInJoin() throws Exception { + disableOmittingJoinOfSuperclassTables(); + assertTranslation( "select x.id from Human h1 join h1.family x" ); //assertTranslation("select index(h2) from Human h1 join h1.family h2"); } @Test public void testManyToManyInSubselect() throws Exception { + disableOmittingJoinOfSuperclassTables(); + assertTranslation( "from Human h1, Human h2 where h2 in (select x.id from h1.family x)" ); assertTranslation( "from Human h1, Human h2 where 'father' in indices(h1.family)" ); } @@ -1580,4 +1625,12 @@ public class HQLTest extends QueryTranslatorTestCase { compileWithAstQueryTranslator( "from Human where name.first = 'Gavin'", false ); } + private void disableOmittingJoinOfSuperclassTables() { + // Disable this feature because of Lacking ClassicQueryTranslatorFactory support + rebuildSessionFactory( c -> c.setProperty( + AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES, + Boolean.FALSE.toString() + ) ); + } + } diff --git a/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinTest.java new file mode 100644 index 0000000000..fee912f180 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinTest.java @@ -0,0 +1,167 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.joinwithoutancestor; + +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +@TestForIssue( jiraKey = "HHH-12993") +public class OmitAncestorJoinTest extends OmitAncestorTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { A.class, SubA.class, SubSubA.class, B.class }; + } + + @Test + public void test() { + // Should not join any parent table + assertFromTables("select valSubA from SubA", SubA.TABLE); + + // Should not join any parent table + assertFromTables("select sa.valSubA from SubA sa", SubA.TABLE); + + // Should omit middle table from inheritance hierarchy + assertFromTables("select ssa.valA from SubSubA ssa", SubSubA.TABLE, A.TABLE); + + // Should omit middle table from inheritance hierarchy + assertFromTables( "select ssa.valA, ssa.valSubSubA from SubSubA ssa", SubSubA.TABLE, A.TABLE ); + + // Should join parent table, because it is used in "where" part + assertFromTables("select valSubA from SubA where valA = 'foo'", SubA.TABLE, A.TABLE); + + // Should join parent table, because it is used in "order by" part + assertFromTables("select valSubSubA from SubSubA order by valA", SubSubA.TABLE, A.TABLE); + + // Should other tables from hierarchye, because it returns whole entity + assertFromTables("select suba from SubA suba", SubA.TABLE, A.TABLE, SubSubA.TABLE); + assertFromTables("from SubA", SubA.TABLE, A.TABLE, SubSubA.TABLE); + + // Should join A table, because it has the reference to B table + assertFromTables( "select suba.b from SubA suba", SubA.TABLE, A.TABLE, B.TABLE ); + assertFromTables( "select suba.b.id from SubA suba", SubA.TABLE, A.TABLE ); + } + + @Entity(name = "A") + @Table(name = A.TABLE) + @Inheritance(strategy = InheritanceType.JOINED) + static class A { + + public static final String TABLE = "A_Table"; + + @Id + @GeneratedValue + private Long id; + + private String valA; + + @ManyToOne + private B b; + + public Long getId() { + return id; + } + + public String getValA() { + return valA; + } + + public void setValA(String valA) { + this.valA = valA; + } + + public B getB() { + return b; + } + + public void setB(B b) { + this.b = b; + } + } + + @Entity(name = "SubA") + @Table(name = SubA.TABLE) + static class SubA extends A { + + public static final String TABLE = "SubA_Table"; + + private String valSubA; + + public String getValSubA() { + return valSubA; + } + + public void setValSubA(String valSubA) { + this.valSubA = valSubA; + } + } + + @Entity(name = "SubSubA") + @Table(name = SubSubA.TABLE) + static class SubSubA extends SubA { + + public static final String TABLE = "SubSubA_Table"; + + private String valSubSubA; + + public String getValSubSubA() { + return valSubSubA; + } + + public void setValSubSubA(String valSubSubA) { + this.valSubSubA = valSubSubA; + } + } + + @Entity(name = "B") + @Table(name = B.TABLE) + static class B { + + public static final String TABLE = "B_table"; + + @Id + @GeneratedValue + private Long id; + + private String valB; + + @OneToMany(mappedBy = "b") + private List aList; + + public Long getId() { + return id; + } + + public String getValB() { + return valB; + } + + public void setValB(String valB) { + this.valB = valB; + } + + public List getaList() { + return aList; + } + + public void setaList(List aList) { + this.aList = aList; + } + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenCommonSecondaryTablePresentTest.java b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenCommonSecondaryTablePresentTest.java new file mode 100644 index 0000000000..70f284e02d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenCommonSecondaryTablePresentTest.java @@ -0,0 +1,262 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.joinwithoutancestor; + +import java.util.List; +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.SecondaryTable; +import javax.persistence.Table; + +import org.hibernate.query.Query; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.transaction.TransactionUtil; +import org.junit.Assert; +import org.junit.Test; + +@TestForIssue(jiraKey = "HHH-12993") +public class OmitAncestorJoinWhenCommonSecondaryTablePresentTest extends OmitAncestorTestCase { + + private static final String SECONDARY_TABLE_NAME = "secondary_table"; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { A.class, SubA.class, B.class, SubB.class, C.class }; + } + + @Override + protected boolean isCleanupTestDataRequired() { + return true; + } + + @Override + protected void cleanupTestData() throws Exception { + TransactionUtil.doInHibernate( this::sessionFactory, s -> { + s.createQuery( "from A", A.class ).list().forEach( s::remove ); + s.createQuery( "from B", B.class ).list().forEach( s::remove ); + s.createQuery( "from C", C.class ).list().forEach( s::remove ); + } ); + super.cleanupTestData(); + } + + @Test + public void shouldNotReturnSecondaryTableValueForSubB() { + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + SubA subA = new SubA( 1L ); + subA.setValSubA( "valSubA" ); + subA.setValSecondaryTable( "valSecondaryTableFromSubA" ); + session.persist( subA ); + + SubB subB = new SubB( 2L ); + subB.setValSubB( "valSubB" ); + subB.setValSecondaryTable( "valSecondaryTableFromSubB" ); + session.persist( subB ); + + Query query = session.createQuery( "select suba.valSecondaryTable from SubA suba", String.class ); + List resultList = query.getResultList(); + Assert.assertEquals( 1, resultList.size() ); + Assert.assertEquals( "valSecondaryTableFromSubA", resultList.get( 0 ) ); + } ); + } + + @Test + public void shouldNotReturnSecondaryTableValueForSubB_implicitJoin() { + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + SubA subA = new SubA( 1L ); + subA.setValSubA( "valSubA" ); + subA.setValSecondaryTable( "valSecondaryTableFromSubA" ); + session.persist( subA ); + + SubB subB = new SubB( 2L ); + subB.setValSubB( "valSubB" ); + subB.setValSecondaryTable( "valSecondaryTableFromSubB" ); + session.persist( subB ); + + C c = new C(); + c.setSubA( subA ); + session.persist( c ); + + Query query = session.createQuery( "select c.subA.valSecondaryTable from C c", String.class ); + List resultList = query.getResultList(); + Assert.assertEquals( 1, resultList.size() ); + Assert.assertEquals( "valSecondaryTableFromSubA", resultList.get( 0 ) ); + } ); + } + + @Entity(name = "A") + @Table(name = A.TABLE) + @Inheritance(strategy = InheritanceType.JOINED) + static class A { + + public static final String TABLE = "A_Table"; + + public A() { + } + + public A(Long id) { + this.id = id; + } + + @Id + private Long id; + + private String valA; + + public Long getId() { + return id; + } + + public String getValA() { + return valA; + } + + public void setValA(String valA) { + this.valA = valA; + } + + } + + @Entity(name = "SubA") + @Table(name = SubA.TABLE) + @SecondaryTable(name = SECONDARY_TABLE_NAME, foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) + static class SubA extends A { + + public static final String TABLE = "SubA_table"; + + public SubA() { + } + + public SubA(Long id) { + super( id ); + } + + private String valSubA; + + @Column(table = SECONDARY_TABLE_NAME) + private String valSecondaryTable; + + public String getValSubA() { + return valSubA; + } + + public void setValSubA(String valSubA) { + this.valSubA = valSubA; + } + + public String getValSecondaryTable() { + return valSecondaryTable; + } + + public void setValSecondaryTable(String valSecondaryTable) { + this.valSecondaryTable = valSecondaryTable; + } + } + + @Entity(name = "B") + @Table(name = B.TABLE) + @Inheritance(strategy = InheritanceType.JOINED) + static class B { + + public static final String TABLE = "B_Table"; + + public B() { + } + + public B(Long id) { + this.id = id; + } + + @Id + private Long id; + + private String valB; + + public Long getId() { + return id; + } + + public String getValB() { + return valB; + } + + public void setValB(String valB) { + this.valB = valB; + } + + } + + @Entity(name = "SubB") + @Table(name = SubB.TABLE) + @SecondaryTable(name = SECONDARY_TABLE_NAME, foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) + static class SubB extends B { + + public static final String TABLE = "SubB_table"; + + public SubB() { + } + + public SubB(Long id) { + super( id ); + } + + private String valSubB; + + @Column(table = SECONDARY_TABLE_NAME) + private String valSecondaryTable; + + public String getValSubB() { + return valSubB; + } + + public void setValSubB(String valSubB) { + this.valSubB = valSubB; + } + + public String getValSecondaryTable() { + return valSecondaryTable; + } + + public void setValSecondaryTable(String valSecondaryTable) { + this.valSecondaryTable = valSecondaryTable; + } + } + + @Entity(name = "C") + @Table(name = C.TABLE) + static class C { + + public static final String TABLE = "C_table"; + + @Id + @GeneratedValue + private Long id; + + @ManyToOne + private SubA subA; + + public Long getId() { + return id; + } + + public SubA getSubA() { + return subA; + } + + public void setSubA(SubA subA) { + this.subA = subA; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenSecondaryTablePresentTest.java b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenSecondaryTablePresentTest.java new file mode 100644 index 0000000000..0a5c8a57cb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorJoinWhenSecondaryTablePresentTest.java @@ -0,0 +1,107 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.joinwithoutancestor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.SecondaryTable; +import javax.persistence.Table; + +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +@TestForIssue(jiraKey = "HHH-12993") +public class OmitAncestorJoinWhenSecondaryTablePresentTest extends OmitAncestorTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { A.class, SubA.class, SubSubA.class }; + } + + @Test + public void test() { + assertFromTables( "select valSubASecondaryTable from SubA", SubA.TABLE, SubSubA.TABLE, SubA.SECONDARY_TABLE ); + } + + @Entity(name = "A") + @Table(name = A.TABLE) + @Inheritance(strategy = InheritanceType.JOINED) + static class A { + + public static final String TABLE = "A_Table"; + + @Id + @GeneratedValue + private Long id; + + private String valA; + + public Long getId() { + return id; + } + + public String getValA() { + return valA; + } + + public void setValA(String valA) { + this.valA = valA; + } + } + + @Entity(name = "SubA") + @Table(name = SubA.TABLE) + @SecondaryTable(name = SubA.SECONDARY_TABLE) + static class SubA extends A { + + public static final String TABLE = "SubA_Table"; + public static final String SECONDARY_TABLE = "SubA_Table_Sec"; + + private String valSubA; + + @Column(table = SECONDARY_TABLE) + private String valSubASecondaryTable; + + public String getValSubA() { + return valSubA; + } + + public void setValSubA(String valSubA) { + this.valSubA = valSubA; + } + + public String getValSubASecondaryTable() { + return valSubASecondaryTable; + } + + public void setValSubASecondaryTable(String valSubASecondaryTable) { + this.valSubASecondaryTable = valSubASecondaryTable; + } + } + + @Entity(name = "SubSubA") + @Table(name = SubSubA.TABLE) + static class SubSubA extends SubA { + + public static final String TABLE = "SubSubA_Table"; + + private String valSubSubA; + + public String getValSubSubA() { + return valSubSubA; + } + + public void setValSubSubA(String valSubSubA) { + this.valSubSubA = valSubSubA; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorTestCase.java b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorTestCase.java new file mode 100644 index 0000000000..5c6d310287 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/OmitAncestorTestCase.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.joinwithoutancestor; + +import org.hibernate.Session; +import org.hibernate.engine.query.spi.HQLQueryPlan; +import org.hibernate.engine.query.spi.QueryPlanCache; +import org.hibernate.hql.spi.QueryTranslator; +import org.hibernate.internal.SessionImpl; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.transaction.TransactionUtil; + +public abstract class OmitAncestorTestCase extends BaseCoreFunctionalTestCase { + + protected void assertFromTables(String query, String... tables) { + try { + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + String sql = getSql( session, query ); + SqlAsserts.assertFromTables( sql, tables ); + session.createQuery( query ).getResultList(); + } ); + } + catch (AssertionError e) { + throw e; + } + } + + protected String getSql(Session session, String hql) { + // Create query + session.createQuery( hql ); + + // Get plan from cache + QueryPlanCache queryPlanCache = sessionFactory().getQueryPlanCache(); + HQLQueryPlan hqlQueryPlan = queryPlanCache.getHQLQueryPlan( + hql, + false, + ( (SessionImpl) session ).getLoadQueryInfluencers().getEnabledFilters() + ); + QueryTranslator queryTranslator = hqlQueryPlan.getTranslators()[0]; + String sql = queryTranslator.getSQLString(); + return sql; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/SqlAsserts.java b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/SqlAsserts.java new file mode 100644 index 0000000000..5932c99352 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/joinwithoutancestor/SqlAsserts.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.joinwithoutancestor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class SqlAsserts { + + public static void assertFromTables(String sql, String... expectedTables) { + List actualTables = parse( sql ); + if ( expectedTables.length == actualTables.size() ) { + boolean diffFound = false; + for ( int i = 0; i < expectedTables.length; i++ ) { + if ( !( expectedTables[i].equals( actualTables.get( i ).name ) ) ) { + diffFound = true; + break; + } + } + if ( !diffFound ) { + return; + } + } + List actualTableNames = actualTables.stream().map( x -> x.name ).collect( Collectors.toList() ); + List expectedTableNames = Arrays.asList( expectedTables ); + throw new AssertionError( "Expected tables: " + expectedTableNames + ", Actual tables: " + actualTableNames ); + } + + private static List
parse(String sql) { + List
result = new ArrayList<>(); + String from = findFrom( sql ); + List commaSeparatedFromParts = findCommaSeparatedFromParts( from ); + for ( String commaSeparatedFromPart : commaSeparatedFromParts ) { + List
tables = findTables( commaSeparatedFromPart ); + result.addAll( tables ); + } + return result; + } + + private static String findFrom(String sqlString) { + Pattern pattern = Pattern.compile( ".*\\s+from\\s+(?.*?)(\\z|(\\s+(where|order|having).*))" ); + Matcher matcher = pattern.matcher( sqlString ); + if ( matcher.matches() ) { + return matcher.group( "frompart" ); + } + else { + throw new RuntimeException( "Can not find from part in sql statement." ); + } + } + + private static List findCommaSeparatedFromParts(String from) { + return Arrays.stream( from.split( "," ) ).map( x -> x.trim() ).collect( Collectors.toList() ); + } + + private static List
findTables(String fromPart) { + List
result = new ArrayList<>(); + result.add( findFirstTable( fromPart ) ); + + String otherTablesPart = findOtherTablesPart( fromPart ); + result.addAll( findOtherTables( otherTablesPart ) ); + + return result; + } + + private static Table findFirstTable(String fromPart) { + Pattern pattern = Pattern.compile( "(?
\\S+)\\s+(?\\S*)\\s*(?.*)" ); + Matcher matcher = pattern.matcher( fromPart ); + if ( matcher.matches() ) { + Table firstTable = new Table( matcher.group( "table" ), matcher.group( "alias" ), false, false ); + return firstTable; + } + else { + throw new RuntimeException( "Can not find the first table in the from part." ); + } + } + + private static String findOtherTablesPart(String fromPart) { + Pattern pattern = Pattern.compile( "(?
\\S+)\\s+(?\\S*)\\s*(?.*)" ); + Matcher matcher = pattern.matcher( fromPart ); + if ( matcher.matches() ) { + String joins = matcher.group( "joins" ); + return joins; + } + else { + throw new RuntimeException( "Can not find joins in the from part." ); + } + } + + private static List
findOtherTables(String otherTablesPart) { + Pattern pattern = Pattern.compile( + "(?join|inner join|left join|cross join|left outer join)\\s+(?
\\S+)\\s+(?\\S+)" ); + Matcher matcher = pattern.matcher( otherTablesPart ); + List
joins = new ArrayList<>(); + while ( matcher.find() ) { + String table = matcher.group( "table" ); + String alias = matcher.group( "alias" ); + String join = matcher.group( "jointype" ); + boolean innerJoin = join.equals( "join" ) || join.equals( "inner join" ); + joins.add( new Table( table, alias, true, innerJoin ) ); + } + return joins; + } + + private static class Table { + String name; + String alias; + boolean join; + boolean innerJoin; + + public Table(String table, String alias, boolean join, boolean innerJoin) { + this.name = table; + this.alias = alias; + this.join = join; + this.innerJoin = innerJoin; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + if ( innerJoin ) { + sb.append( "inner join " ); + } + else if ( join ) { + sb.append( "join " ); + } + sb.append( name ); + sb.append( " " ); + sb.append( alias ); + return sb.toString(); + } + } +} From 19ac013eeb666a45066b6adb23c05df80a0fe6bb Mon Sep 17 00:00:00 2001 From: Carsten Hammer Date: Sun, 18 Aug 2019 12:25:07 +0200 Subject: [PATCH 42/99] HHH-13591 Replaces simple uses of array iteration with a corresponding for-each loop --- .../internal/bytebuddy/CodeTemplates.java | 36 +++++++++---------- .../java/org/hibernate/cfg/BinderHelper.java | 14 ++++---- .../cfg/ExternalSessionFactoryConfig.java | 4 +-- .../cfg/IndexOrUniqueKeySecondPass.java | 4 +-- .../engine/query/spi/HQLQueryPlan.java | 4 +-- .../engine/spi/NamedSQLQueryDefinition.java | 4 +-- .../hql/internal/ast/tree/FromElement.java | 11 +++--- .../hql/internal/ast/tree/IntoClause.java | 8 ++--- .../spi/id/TableBasedUpdateHandlerImpl.java | 4 +-- .../cte/CteValuesListUpdateHandlerImpl.java | 5 ++- .../AbstractInlineIdsUpdateHandlerImpl.java | 5 ++- .../entity/AbstractEntityPersister.java | 4 +-- .../main/java/org/hibernate/sql/Insert.java | 4 +-- .../java/org/hibernate/sql/InsertSelect.java | 4 +-- .../org/hibernate/sql/QueryJoinFragment.java | 8 ++--- ...tionExtractorJdbcDatabaseMetaDataImpl.java | 4 +-- .../tuple/entity/EntityMetamodel.java | 4 +-- .../org/hibernate/test/legacy/FumTest.java | 4 +-- .../ImplicitCompositeKeyJoinTest.java | 4 +-- ...SubselectFetchCollectionFromBatchTest.java | 24 ++++++------- .../testing/junit4/ExtraAssertions.java | 3 +- 21 files changed, 77 insertions(+), 85 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java index da3fdc33a7..66639b27d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java @@ -358,9 +358,9 @@ class CodeTemplates { static void enter(@FieldValue Collection field, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( argument == null || !argument.contains( array[i] ) ) { - setterNull( array[i], null ); + for (Object array1 : array) { + if (argument == null || !argument.contains(array1)) { + setterNull(array1, null); } } } @@ -370,9 +370,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) && getter( array[i] ) != self ) { - setterSelf( array[i], self ); + for (Object array1 : array) { + if (Hibernate.isPropertyInitialized(array1, mappedBy) && getter(array1) != self) { + setterSelf(array1, self); } } } @@ -399,9 +399,9 @@ class CodeTemplates { static void enter(@FieldValue Map field, @Advice.Argument(0) Map argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.values().toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( argument == null || !argument.values().contains( array[i] ) ) { - setterNull( array[i], null ); + for (Object array1 : array) { + if (argument == null || !argument.values().contains(array1)) { + setterNull(array1, null); } } } @@ -411,9 +411,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Map argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.values().toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) && getter( array[i] ) != self ) { - setterSelf( array[i], self ); + for (Object array1 : array) { + if (Hibernate.isPropertyInitialized(array1, mappedBy) && getter(array1) != self) { + setterSelf(array1, self); } } } @@ -467,9 +467,9 @@ class CodeTemplates { static void enter(@Advice.This Object self, @FieldValue Collection field, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( argument == null || !argument.contains( array[i] ) ) { - getter( array[i] ).remove( self ); + for (Object array1 : array) { + if (argument == null || !argument.contains(array1)) { + getter(array1).remove(self); } } } @@ -479,9 +479,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.toArray(); - for ( int i = 0; i < array.length; i++ ) { - if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) ) { - Collection c = getter( array[i] ); + for (Object array1 : array) { + if (Hibernate.isPropertyInitialized(array1, mappedBy)) { + Collection c = getter(array1); if ( c != self && c != null ) { c.add( self ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index 8147bd8ed5..21626d409c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -1124,19 +1124,19 @@ public class BinderHelper { public static Map toAliasTableMap(SqlFragmentAlias[] aliases){ Map ret = new HashMap<>(); - for ( int i = 0; i < aliases.length; i++ ) { - if ( StringHelper.isNotEmpty( aliases[i].table() ) ) { - ret.put( aliases[i].alias(), aliases[i].table() ); -} + for (SqlFragmentAlias aliase : aliases) { + if (StringHelper.isNotEmpty(aliase.table())) { + ret.put(aliase.alias(), aliase.table()); + } } return ret; } public static Map toAliasEntityMap(SqlFragmentAlias[] aliases){ Map ret = new HashMap<>(); - for ( int i = 0; i < aliases.length; i++ ) { - if ( aliases[i].entity() != void.class ) { - ret.put( aliases[i].alias(), aliases[i].entity().getName() ); + for (SqlFragmentAlias aliase : aliases) { + if (aliase.entity() != void.class) { + ret.put(aliase.alias(), aliase.entity().getName()); } } return ret; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java b/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java index 6cb5815061..b4ba00182f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java @@ -247,8 +247,8 @@ public abstract class ExternalSessionFactoryConfig { String[] mappingFiles = ConfigurationHelper.toStringArray( mapResources, " ,\n\t\r\f" ); - for ( int i = 0; i < mappingFiles.length; i++ ) { - cfg.addResource( mappingFiles[i] ); + for (String mappingFile : mappingFiles) { + cfg.addResource(mappingFile); } return cfg; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java index 6bdc9edef3..c1a86d1f88 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java @@ -66,8 +66,8 @@ public class IndexOrUniqueKeySecondPass implements SecondPass { @Override public void doSecondPass(Map persistentClasses) throws MappingException { if ( columns != null ) { - for ( int i = 0; i < columns.length; i++ ) { - addConstraintToColumn( columns[i] ); + for (String column1 : columns) { + addConstraintToColumn(column1); } } if ( column != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java index 6b0b954c21..c142691fe5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java @@ -163,8 +163,8 @@ public class HQLQueryPlan implements Serializable { */ public String[] getSqlStrings() { List sqlStrings = new ArrayList<>(); - for ( int i = 0; i < translators.length; i++ ) { - sqlStrings.addAll( translators[i].collectSqlStrings() ); + for (QueryTranslator translator : translators) { + sqlStrings.addAll(translator.collectSqlStrings()); } return ArrayHelper.toStringArray( sqlStrings ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java index 42ea03bb1e..2210cae617 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java @@ -236,8 +236,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition { allQueryReturns[i] = this.queryReturns[i]; } - for ( int j = 0; j < queryReturnsToAdd.length; j++ ) { - allQueryReturns[i] = queryReturnsToAdd[j]; + for (NativeSQLQueryReturn queryReturnsToAdd1 : queryReturnsToAdd) { + allQueryReturns[i] = queryReturnsToAdd1; i++; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index 960a7d073e..007ae1b5b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -343,13 +343,10 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa final String[] propertyNames = getIdentifierPropertyNames(); List columns = new ArrayList<>(); final boolean inSelect = getWalker().getStatementType() == HqlSqlTokenTypes.SELECT; - for ( int i = 0; i < propertyNames.length; i++ ) { - String[] propertyNameColumns = toColumns( - table, propertyNames[i], - inSelect - ); - for ( int j = 0; j < propertyNameColumns.length; j++ ) { - columns.add( propertyNameColumns[j] ); + for (String propertyName : propertyNames) { + String[] propertyNameColumns = toColumns(table, propertyName, inSelect); + for (String propertyNameColumn : propertyNameColumns) { + columns.add(propertyNameColumn); } } return columns.toArray( new String[columns.size()] ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java index 65a82d1724..2e34fab7d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java @@ -161,8 +161,8 @@ public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode { if ( componentIds == null ) { String[] propertyNames = ( (CompositeType) persister.getIdentifierType() ).getPropertyNames(); componentIds = new HashSet(); - for ( int i = 0; i < propertyNames.length; i++ ) { - componentIds.add( propertyNames[i] ); + for (String propertyName : propertyNames) { + componentIds.add(propertyName); } } if ( componentIds.contains( name ) ) { @@ -194,8 +194,8 @@ public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode { } private void renderColumns(String[] columnNames) { - for ( int i = 0; i < columnNames.length; i++ ) { - columnSpec += columnNames[i] + ", "; + for (String columnName : columnNames) { + columnSpec += columnName + ", "; } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java index 63f88f40e4..6f9706cf18 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java @@ -143,8 +143,8 @@ public class TableBasedUpdateHandlerImpl ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( updates[i], false ); if ( assignmentParameterSpecifications[i] != null ) { int position = 1; // jdbc params are 1-based - for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) { - position += assignmentParameterSpecifications[i][x].bind( ps, queryParameters, session, position ); + for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { + position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); } handleAddedParametersOnUpdate( ps, session, position ); } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java index 532440edb8..544ac688d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java @@ -112,9 +112,8 @@ public class CteValuesListUpdateHandlerImpl } } if ( assignmentParameterSpecifications[i] != null ) { - for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) { - position += assignmentParameterSpecifications[i][x] - .bind( ps, queryParameters, session, position ); + for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { + position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); } } session diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java index 5086fb132d..d4c08d4548 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java @@ -103,9 +103,8 @@ public abstract class AbstractInlineIdsUpdateHandlerImpl .prepareStatement( update, false )) { int position = 1; // jdbc params are 1-based if ( assignmentParameterSpecifications[i] != null ) { - for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) { - position += assignmentParameterSpecifications[i][x] - .bind( ps, queryParameters, session, position ); + for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { + position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); } } jdbcCoordinator.getResultSetReturn() diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 9bd6ebb333..11cfba373d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -5264,8 +5264,8 @@ public abstract class AbstractEntityPersister if ( attribute.getType() instanceof ComponentType ) { final ComponentType type = (ComponentType) attribute.getType(); final ValueGeneration[] propertyValueGenerationStrategies = type.getPropertyValueGenerationStrategies(); - for ( int i = 0; i < propertyValueGenerationStrategies.length; i++ ) { - if ( isReadRequired( propertyValueGenerationStrategies[i], matchTiming ) ) { + for (ValueGeneration propertyValueGenerationStrategie : propertyValueGenerationStrategies) { + if (isReadRequired(propertyValueGenerationStrategie, matchTiming)) { return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java index 21bae46d67..7dc47b70f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java @@ -42,8 +42,8 @@ public class Insert { } public Insert addColumns(String[] columnNames) { - for ( int i=0; i 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) - Hibernate.initialize( groups[ i ] ); - assertTrue( Hibernate.isInitialized( groups[i] ) ); + Hibernate.initialize(group); + assertTrue(Hibernate.isInitialized(group)); // the collections should be uninitialized - assertFalse( Hibernate.isInitialized( groups[i].getEmployees() ) ); + assertFalse(Hibernate.isInitialized(group.getEmployees())); } // both Group proxies should have been loaded in the same batch; @@ -250,19 +250,19 @@ public class SubselectFetchCollectionFromBatchTest extends BaseCoreFunctionalTe assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - for (int i = 0 ; i < groups.length; i++ ) { + for (EmployeeGroup group : groups) { // Both groups get initialized and are added to the PersistenceContext when i == 0; // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) - Hibernate.initialize( groups[ i ] ); - assertTrue( Hibernate.isInitialized( groups[i] ) ); - assertTrue( Hibernate.isInitialized( groups[i].getLead() ) ); - assertFalse( Hibernate.isInitialized( groups[i].getLead().getCollaborators() ) ); - assertTrue( Hibernate.isInitialized( groups[i].getManager() ) ); - assertFalse( Hibernate.isInitialized( groups[i].getManager().getCollaborators() ) ); + Hibernate.initialize(group); + assertTrue(Hibernate.isInitialized(group)); + assertTrue(Hibernate.isInitialized(group.getLead())); + assertFalse(Hibernate.isInitialized(group.getLead().getCollaborators())); + assertTrue(Hibernate.isInitialized(group.getManager())); + assertFalse(Hibernate.isInitialized(group.getManager().getCollaborators())); // the collections should be uninitialized - assertFalse( Hibernate.isInitialized( groups[i].getEmployees() ) ); + assertFalse(Hibernate.isInitialized(group.getEmployees())); } // both Group proxies should have been loaded in the same batch; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java index 491356db20..7e3b495dd3 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java @@ -73,8 +73,7 @@ public final class ExtraAssertions { private static Map generateJdbcTypeCache() { final Field[] fields = Types.class.getFields(); Map cache = new HashMap( (int)( fields.length * .75 ) + 1 ); - for ( int i = 0; i < fields.length; i++ ) { - final Field field = fields[i]; + for (Field field : fields) { if ( Modifier.isStatic( field.getModifiers() ) ) { try { cache.put( field.get( null ), field.getName() ); From 4661efa468c11b63e992a1c7a84c959c1477033b Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 20 Aug 2019 08:41:00 +0100 Subject: [PATCH 43/99] HHH-13591 Fixing formatting of previous patch --- .../internal/bytebuddy/CodeTemplates.java | 40 +++++++++---------- .../java/org/hibernate/cfg/BinderHelper.java | 12 +++--- .../cfg/ExternalSessionFactoryConfig.java | 4 +- .../cfg/IndexOrUniqueKeySecondPass.java | 4 +- .../engine/query/spi/HQLQueryPlan.java | 4 +- .../engine/spi/NamedSQLQueryDefinition.java | 2 +- .../hql/internal/ast/tree/FromElement.java | 8 ++-- .../hql/internal/ast/tree/IntoClause.java | 6 +-- .../spi/id/TableBasedUpdateHandlerImpl.java | 5 ++- .../cte/CteValuesListUpdateHandlerImpl.java | 5 ++- .../AbstractInlineIdsUpdateHandlerImpl.java | 5 ++- .../entity/AbstractEntityPersister.java | 4 +- .../main/java/org/hibernate/sql/Insert.java | 10 ++--- .../java/org/hibernate/sql/InsertSelect.java | 4 +- .../org/hibernate/sql/QueryJoinFragment.java | 8 ++-- ...tionExtractorJdbcDatabaseMetaDataImpl.java | 4 +- .../tuple/entity/EntityMetamodel.java | 4 +- .../org/hibernate/test/legacy/FumTest.java | 32 +++++++-------- .../ImplicitCompositeKeyJoinTest.java | 13 +++--- ...SubselectFetchCollectionFromBatchTest.java | 32 +++++++-------- .../testing/junit4/ExtraAssertions.java | 2 +- 21 files changed, 105 insertions(+), 103 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java index 66639b27d4..2c870b1844 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java @@ -20,10 +20,10 @@ import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; import org.hibernate.bytecode.enhance.spi.CollectionTracker; import org.hibernate.bytecode.enhance.spi.EnhancerConstants; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; -import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker; -import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.CompositeOwner; import org.hibernate.engine.spi.CompositeTracker; +import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; import net.bytebuddy.asm.Advice; @@ -358,9 +358,9 @@ class CodeTemplates { static void enter(@FieldValue Collection field, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.toArray(); - for (Object array1 : array) { - if (argument == null || !argument.contains(array1)) { - setterNull(array1, null); + for ( Object array1 : array ) { + if ( argument == null || !argument.contains( array1 ) ) { + setterNull( array1, null ); } } } @@ -370,9 +370,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.toArray(); - for (Object array1 : array) { - if (Hibernate.isPropertyInitialized(array1, mappedBy) && getter(array1) != self) { - setterSelf(array1, self); + for ( Object array1 : array ) { + if ( Hibernate.isPropertyInitialized( array1, mappedBy ) && getter( array1 ) != self ) { + setterSelf( array1, self ); } } } @@ -399,9 +399,9 @@ class CodeTemplates { static void enter(@FieldValue Map field, @Advice.Argument(0) Map argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.values().toArray(); - for (Object array1 : array) { - if (argument == null || !argument.values().contains(array1)) { - setterNull(array1, null); + for ( Object array1 : array ) { + if ( argument == null || !argument.values().contains( array1 ) ) { + setterNull( array1, null ); } } } @@ -411,9 +411,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Map argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.values().toArray(); - for (Object array1 : array) { - if (Hibernate.isPropertyInitialized(array1, mappedBy) && getter(array1) != self) { - setterSelf(array1, self); + for ( Object array1 : array ) { + if ( Hibernate.isPropertyInitialized( array1, mappedBy ) && getter( array1 ) != self ) { + setterSelf( array1, self ); } } } @@ -467,9 +467,9 @@ class CodeTemplates { static void enter(@Advice.This Object self, @FieldValue Collection field, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) { Object[] array = field.toArray(); - for (Object array1 : array) { - if (argument == null || !argument.contains(array1)) { - getter(array1).remove(self); + for ( Object array1 : array ) { + if ( argument == null || !argument.contains( array1 ) ) { + getter( array1 ).remove( self ); } } } @@ -479,9 +479,9 @@ class CodeTemplates { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection argument, @MappedBy String mappedBy) { if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) { Object[] array = argument.toArray(); - for (Object array1 : array) { - if (Hibernate.isPropertyInitialized(array1, mappedBy)) { - Collection c = getter(array1); + for ( Object array1 : array ) { + if ( Hibernate.isPropertyInitialized( array1, mappedBy ) ) { + Collection c = getter( array1 ); if ( c != self && c != null ) { c.add( self ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index 21626d409c..3e7ea54c68 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -1124,9 +1124,9 @@ public class BinderHelper { public static Map toAliasTableMap(SqlFragmentAlias[] aliases){ Map ret = new HashMap<>(); - for (SqlFragmentAlias aliase : aliases) { - if (StringHelper.isNotEmpty(aliase.table())) { - ret.put(aliase.alias(), aliase.table()); + for ( SqlFragmentAlias aliase : aliases ) { + if ( StringHelper.isNotEmpty( aliase.table() ) ) { + ret.put( aliase.alias(), aliase.table() ); } } return ret; @@ -1134,9 +1134,9 @@ public class BinderHelper { public static Map toAliasEntityMap(SqlFragmentAlias[] aliases){ Map ret = new HashMap<>(); - for (SqlFragmentAlias aliase : aliases) { - if (aliase.entity() != void.class) { - ret.put(aliase.alias(), aliase.entity().getName()); + for ( SqlFragmentAlias aliase : aliases ) { + if ( aliase.entity() != void.class ) { + ret.put( aliase.alias(), aliase.entity().getName() ); } } return ret; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java b/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java index b4ba00182f..4121cb2bfe 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ExternalSessionFactoryConfig.java @@ -247,8 +247,8 @@ public abstract class ExternalSessionFactoryConfig { String[] mappingFiles = ConfigurationHelper.toStringArray( mapResources, " ,\n\t\r\f" ); - for (String mappingFile : mappingFiles) { - cfg.addResource(mappingFile); + for ( String mappingFile : mappingFiles ) { + cfg.addResource( mappingFile ); } return cfg; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java index c1a86d1f88..d5ae2c38b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/IndexOrUniqueKeySecondPass.java @@ -66,8 +66,8 @@ public class IndexOrUniqueKeySecondPass implements SecondPass { @Override public void doSecondPass(Map persistentClasses) throws MappingException { if ( columns != null ) { - for (String column1 : columns) { - addConstraintToColumn(column1); + for ( String column1 : columns ) { + addConstraintToColumn( column1 ); } } if ( column != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java index c142691fe5..1ba9dbe6e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java @@ -163,8 +163,8 @@ public class HQLQueryPlan implements Serializable { */ public String[] getSqlStrings() { List sqlStrings = new ArrayList<>(); - for (QueryTranslator translator : translators) { - sqlStrings.addAll(translator.collectSqlStrings()); + for ( QueryTranslator translator : translators ) { + sqlStrings.addAll( translator.collectSqlStrings() ); } return ArrayHelper.toStringArray( sqlStrings ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java index 2210cae617..ea5fa3ecbb 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/NamedSQLQueryDefinition.java @@ -236,7 +236,7 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition { allQueryReturns[i] = this.queryReturns[i]; } - for (NativeSQLQueryReturn queryReturnsToAdd1 : queryReturnsToAdd) { + for ( NativeSQLQueryReturn queryReturnsToAdd1 : queryReturnsToAdd ) { allQueryReturns[i] = queryReturnsToAdd1; i++; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index 007ae1b5b6..6470c33d99 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -343,10 +343,10 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa final String[] propertyNames = getIdentifierPropertyNames(); List columns = new ArrayList<>(); final boolean inSelect = getWalker().getStatementType() == HqlSqlTokenTypes.SELECT; - for (String propertyName : propertyNames) { - String[] propertyNameColumns = toColumns(table, propertyName, inSelect); - for (String propertyNameColumn : propertyNameColumns) { - columns.add(propertyNameColumn); + for ( String propertyName : propertyNames ) { + String[] propertyNameColumns = toColumns( table, propertyName, inSelect ); + for ( String propertyNameColumn : propertyNameColumns ) { + columns.add( propertyNameColumn ); } } return columns.toArray( new String[columns.size()] ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java index 2e34fab7d7..02c38142d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IntoClause.java @@ -161,8 +161,8 @@ public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode { if ( componentIds == null ) { String[] propertyNames = ( (CompositeType) persister.getIdentifierType() ).getPropertyNames(); componentIds = new HashSet(); - for (String propertyName : propertyNames) { - componentIds.add(propertyName); + for ( String propertyName : propertyNames ) { + componentIds.add( propertyName ); } } if ( componentIds.contains( name ) ) { @@ -194,7 +194,7 @@ public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode { } private void renderColumns(String[] columnNames) { - for (String columnName : columnNames) { + for ( String columnName : columnNames ) { columnSpec += columnName + ", "; } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java index 6f9706cf18..6b47360d04 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/TableBasedUpdateHandlerImpl.java @@ -143,8 +143,9 @@ public class TableBasedUpdateHandlerImpl ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( updates[i], false ); if ( assignmentParameterSpecifications[i] != null ) { int position = 1; // jdbc params are 1-based - for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { - position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); + for ( ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i] ) { + position += assignmentParameterSpecification + .bind( ps, queryParameters, session, position ); } handleAddedParametersOnUpdate( ps, session, position ); } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java index 544ac688d1..7fcc813233 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/cte/CteValuesListUpdateHandlerImpl.java @@ -112,8 +112,9 @@ public class CteValuesListUpdateHandlerImpl } } if ( assignmentParameterSpecifications[i] != null ) { - for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { - position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); + for ( ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i] ) { + position += assignmentParameterSpecification + .bind( ps, queryParameters, session, position ); } } session diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java index d4c08d4548..8db4752a96 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/id/inline/AbstractInlineIdsUpdateHandlerImpl.java @@ -103,8 +103,9 @@ public abstract class AbstractInlineIdsUpdateHandlerImpl .prepareStatement( update, false )) { int position = 1; // jdbc params are 1-based if ( assignmentParameterSpecifications[i] != null ) { - for (ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i]) { - position += assignmentParameterSpecification.bind(ps, queryParameters, session, position); + for ( ParameterSpecification assignmentParameterSpecification : assignmentParameterSpecifications[i] ) { + position += assignmentParameterSpecification + .bind( ps, queryParameters, session, position ); } } jdbcCoordinator.getResultSetReturn() diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 11cfba373d..d2b78ee95f 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -5264,8 +5264,8 @@ public abstract class AbstractEntityPersister if ( attribute.getType() instanceof ComponentType ) { final ComponentType type = (ComponentType) attribute.getType(); final ValueGeneration[] propertyValueGenerationStrategies = type.getPropertyValueGenerationStrategies(); - for (ValueGeneration propertyValueGenerationStrategie : propertyValueGenerationStrategies) { - if (isReadRequired(propertyValueGenerationStrategie, matchTiming)) { + for ( ValueGeneration propertyValueGenerationStrategie : propertyValueGenerationStrategies ) { + if ( isReadRequired( propertyValueGenerationStrategie, matchTiming ) ) { return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java index 7dc47b70f9..25b2d7d276 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java @@ -38,12 +38,12 @@ public class Insert { } public Insert addColumn(String columnName) { - return addColumn(columnName, "?"); + return addColumn( columnName, "?" ); } public Insert addColumns(String[] columnNames) { - for (String columnName : columnNames) { - addColumn(columnName); + for ( String columnName : columnNames ) { + addColumn( columnName ); } return this; } @@ -67,12 +67,12 @@ public class Insert { } public Insert addColumn(String columnName, String valueExpression) { - columns.put(columnName, valueExpression); + columns.put( columnName, valueExpression ); return this; } public Insert addColumn(String columnName, Object value, LiteralType type) throws Exception { - return addColumn( columnName, type.objectToSQLString(value, dialect) ); + return addColumn( columnName, type.objectToSQLString( value, dialect ) ); } public Insert addIdentityColumn(String columnName) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/InsertSelect.java b/hibernate-core/src/main/java/org/hibernate/sql/InsertSelect.java index f242d2872a..efc4a7cf49 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/InsertSelect.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/InsertSelect.java @@ -44,8 +44,8 @@ public class InsertSelect { } public InsertSelect addColumns(String[] columnNames) { - for (String columnName : columnNames) { - this.columnNames.add(columnName); + for ( String columnName : columnNames ) { + this.columnNames.add( columnName ); } return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/QueryJoinFragment.java b/hibernate-core/src/main/java/org/hibernate/sql/QueryJoinFragment.java index 093465f049..2909f7cd7c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/QueryJoinFragment.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/QueryJoinFragment.java @@ -88,10 +88,12 @@ public class QueryJoinFragment extends JoinFragment { } public void addCondition(String alias, String[] columns, String condition) { - for (String column : columns) { + for ( String column : columns ) { afterWhere.append( " and " ) - .append( alias ) - .append( '.' ).append(column).append(condition); + .append( alias ) + .append( '.' ) + .append( column ) + .append( condition ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java index 562ec664a1..1a4bf472cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java @@ -530,8 +530,8 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information if ( "TABLE".equalsIgnoreCase( tableType ) ) { return true; } - for (String extraPhysicalTableType : extraPhysicalTableTypes) { - if (extraPhysicalTableType.equalsIgnoreCase(tableType)) { + for ( String extraPhysicalTableType : extraPhysicalTableTypes ) { + if ( extraPhysicalTableType.equalsIgnoreCase( tableType ) ) { return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 4c792b84c0..0e4de4531e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -832,8 +832,8 @@ public class EntityMetamodel implements Serializable { } else if ( type.isComponentType() ) { Type[] subtypes = ( (CompositeType) type ).getSubtypes(); - for (Type subtype : subtypes) { - if (indicatesCollection(subtype)) { + for ( Type subtype : subtypes ) { + if ( indicatesCollection( subtype ) ) { return true; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/FumTest.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/FumTest.java index e267275f95..7fffc1a267 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/FumTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/FumTest.java @@ -8,11 +8,6 @@ //$Id: FumTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $ package org.hibernate.test.legacy; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -21,7 +16,6 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -51,18 +45,22 @@ import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.PointbaseDialect; import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.dialect.TimesTenDialect; - -import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.RequiresDialectFeature; -import org.hibernate.testing.SkipForDialect; import org.hibernate.transform.Transformers; -import org.hibernate.type.CalendarType; import org.hibernate.type.EntityType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StringType; import org.hibernate.type.Type; + +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.SkipForDialect; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + @RequiresDialectFeature(DialectChecks.SupportsNoColumnInsert.class) public class FumTest extends LegacyTestCase { private static short fumKeyShort = 1; @@ -470,13 +468,13 @@ public class FumTest extends LegacyTestCase { ); Query qu = s.createQuery("select fum.fum, fum , fum.fum from Fum fum"); Type[] types = qu.getReturnTypes(); - assertTrue(types.length==3); - for (Type type : types) { - assertTrue(type != null); + assertTrue( types.length == 3 ); + for ( Type type : types ) { + assertTrue( type != null ); } - assertTrue(types[0] instanceof StringType); - assertTrue(types[1] instanceof EntityType); - assertTrue(types[2] instanceof StringType); + assertTrue( types[0] instanceof StringType ); + assertTrue( types[1] instanceof EntityType ); + assertTrue( types[2] instanceof StringType ); Iterator iter = qu.iterate(); int j = 0; while ( iter.hasNext() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/ImplicitCompositeKeyJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/ImplicitCompositeKeyJoinTest.java index c24e3658c7..4f59647beb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/ImplicitCompositeKeyJoinTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/ImplicitCompositeKeyJoinTest.java @@ -6,16 +6,14 @@ */ package org.hibernate.test.schemaupdate; +import java.io.Serializable; +import java.util.List; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.ManyToOne; import javax.persistence.Table; -import java.io.Serializable; -import java.util.List; - -import org.apache.log4j.Logger; import org.hibernate.annotations.ForeignKey; import org.hibernate.boot.MetadataSources; @@ -23,9 +21,10 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.tool.schema.internal.SchemaCreatorImpl; +import org.hibernate.testing.TestForIssue; import org.junit.Test; -import org.hibernate.testing.TestForIssue; +import org.apache.log4j.Logger; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -57,8 +56,8 @@ public class ImplicitCompositeKeyJoinTest { if ( command.toLowerCase().matches( "^create( (column|row))? table employee.+" ) ) { final String[] columnsDefinition = getColumnsDefinition( command ); - for (String columnsDefinition1 : columnsDefinition) { - checkColumnSize(columnsDefinition1); + for ( String columnsDefinition1 : columnsDefinition ) { + checkColumnSize( columnsDefinition1 ); } createTableEmployeeFound = true; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java b/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java index 9f2c8b3ac1..82e3cdca7a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java @@ -35,8 +35,6 @@ import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; -import org.junit.Test; - import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.Transaction; @@ -45,8 +43,10 @@ import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -102,22 +102,22 @@ public class SubselectFetchCollectionFromBatchTest extends BaseCoreFunctionalTe assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - for (EmployeeGroup group : groups) { + for ( EmployeeGroup group : groups ) { // Both groups get initialized and are added to the PersistenceContext when i == 0; // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) - Hibernate.initialize(group); - assertTrue(Hibernate.isInitialized(group)); + Hibernate.initialize( group ); + assertTrue( Hibernate.isInitialized( group ) ); // the collections should be uninitialized - assertFalse(Hibernate.isInitialized(group.getEmployees())); + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } // both Group proxies should have been loaded in the same batch; assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); - for (EmployeeGroup group : groups) { + for ( EmployeeGroup group : groups ) { assertTrue( Hibernate.isInitialized( group ) ); assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } @@ -250,26 +250,26 @@ public class SubselectFetchCollectionFromBatchTest extends BaseCoreFunctionalTe assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - for (EmployeeGroup group : groups) { + for ( EmployeeGroup group : groups ) { // Both groups get initialized and are added to the PersistenceContext when i == 0; // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) - Hibernate.initialize(group); - assertTrue(Hibernate.isInitialized(group)); - assertTrue(Hibernate.isInitialized(group.getLead())); - assertFalse(Hibernate.isInitialized(group.getLead().getCollaborators())); - assertTrue(Hibernate.isInitialized(group.getManager())); - assertFalse(Hibernate.isInitialized(group.getManager().getCollaborators())); + Hibernate.initialize( group ); + assertTrue( Hibernate.isInitialized( group ) ); + assertTrue( Hibernate.isInitialized( group.getLead() ) ); + assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); + assertTrue( Hibernate.isInitialized( group.getManager() ) ); + assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); // the collections should be uninitialized - assertFalse(Hibernate.isInitialized(group.getEmployees())); + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } // both Group proxies should have been loaded in the same batch; assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); - for (EmployeeGroup group : groups) { + for ( EmployeeGroup group : groups ) { assertTrue( Hibernate.isInitialized( group ) ); assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java index 7e3b495dd3..d8bbf697c5 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/ExtraAssertions.java @@ -73,7 +73,7 @@ public final class ExtraAssertions { private static Map generateJdbcTypeCache() { final Field[] fields = Types.class.getFields(); Map cache = new HashMap( (int)( fields.length * .75 ) + 1 ); - for (Field field : fields) { + for ( Field field : fields ) { if ( Modifier.isStatic( field.getModifiers() ) ) { try { cache.put( field.get( null ), field.getName() ); From 408275ddecc3ba79bfddbe78a2867116c52e6423 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 20 Aug 2019 18:53:09 +0100 Subject: [PATCH 44/99] HHH-13594 ResourceRegistryStandardImpl#release could avoid allocating a capturing lambda --- .../internal/ResourceRegistryStandardImpl.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java index a40d738eda..492a53c811 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java @@ -146,13 +146,18 @@ public final class ResourceRegistryStandardImpl implements ResourceRegistry { close( resultSet ); } - protected void closeAll(final HashMap resultSets) { + private static void closeAll(final HashMap resultSets) { resultSets.forEach( (resultSet, o) -> close( resultSet ) ); resultSets.clear(); } + private static void releaseXref(final Statement s, final HashMap r) { + closeAll( r ); + close( s ); + } + @SuppressWarnings({"unchecked"}) - public static void close(ResultSet resultSet) { + private static void close(final ResultSet resultSet) { log.tracef( "Closing result set [%s]", resultSet ); try { @@ -315,12 +320,7 @@ public final class ResourceRegistryStandardImpl implements ResourceRegistry { jdbcObserver.jdbcReleaseRegistryResourcesStart(); } - xref.forEach( - (Statement s, HashMap r) -> { - closeAll( r ); - close( s ); - } - ); + xref.forEach( ResourceRegistryStandardImpl::releaseXref ); xref.clear(); closeAll( unassociatedResultSets ); From 5eaa1498df0f8f5f7f00515fb9bb2960b2ac489b Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 9 Aug 2019 10:28:18 +0100 Subject: [PATCH 45/99] HHH-13565 Session opening efficiency: introduce FastSessionServices and design for shared services among sessions --- .../AbstractSharedSessionContract.java | 3 + .../internal/FastSessionServices.java | 171 ++++++++++++++++++ .../internal/SessionFactoryImpl.java | 12 ++ .../org/hibernate/internal/SessionImpl.java | 62 +++---- 4 files changed, 209 insertions(+), 39 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index bf37e43d55..4cc33fa7ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -116,6 +116,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont private transient SessionFactoryImpl factory; private final String tenantIdentifier; + protected transient FastSessionServices fastSessionServices; private UUID sessionIdentifier; private transient JdbcConnectionAccess jdbcConnectionAccess; @@ -154,6 +155,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreationOptions options) { this.factory = factory; + this.fastSessionServices = factory.getFastSessionServices(); this.cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); this.disallowOutOfTransactionUpdateOperations = !factory.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); @@ -1226,6 +1228,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont // -- see above factory = SessionFactoryImpl.deserialize( ois ); + fastSessionServices = factory.getFastSessionServices(); jdbcSessionContext = new JdbcSessionContextImpl( this, (StatementInspector) ois.readObject() ); jdbcCoordinator = JdbcCoordinatorImpl.deserialize( ois, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java new file mode 100644 index 0000000000..020d05c7e1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -0,0 +1,171 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.internal; + +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.AutoFlushEventListener; +import org.hibernate.event.spi.ClearEventListener; +import org.hibernate.event.spi.DeleteEventListener; +import org.hibernate.event.spi.DirtyCheckEventListener; +import org.hibernate.event.spi.EventType; +import org.hibernate.event.spi.EvictEventListener; +import org.hibernate.event.spi.FlushEventListener; +import org.hibernate.event.spi.InitializeCollectionEventListener; +import org.hibernate.event.spi.LoadEventListener; +import org.hibernate.event.spi.LockEventListener; +import org.hibernate.event.spi.MergeEventListener; +import org.hibernate.event.spi.PersistEventListener; +import org.hibernate.event.spi.RefreshEventListener; +import org.hibernate.event.spi.ReplicateEventListener; +import org.hibernate.event.spi.ResolveNaturalIdEventListener; +import org.hibernate.event.spi.SaveOrUpdateEventListener; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +import java.util.Objects; + +/** + * Internal component. + * + * Collects any components that any Session implementation will likely need + * for faster access and reduced allocations. + * Conceptually this acts as an immutable caching intermediary between Session + * and SessionFactory. + * Designed to be immutable, and shared across Session instances. + * + * Assumes to be created infrequently, possibly only once per SessionFactory. + * + * If the Session is requiring to retrieve (or compute) anything from the SessionFactory, + * and this computation would result in the same outcome for any Session created on + * this same SessionFactory, then it belongs in a final field of this class. + * + * Finally, consider also limiting the size of each Session: some fields could be good + * candidates to be replaced with access via this object. + * + * @author Sanne Grinovero + */ +final class FastSessionServices { + + private final Iterable clearEventListeners; + private final Iterable saveUpdateEventListeners; + private final Iterable evictEventListeners; + private final Iterable saveEventListeners; + private final Iterable updateEventListeners; + private final Iterable lockEventListeners; + private final Iterable persistEventListeners; + private final Iterable persistOnFlushEventListeners; + private final Iterable mergeEventListeners; + private final Iterable deleteEventListeners; + private final Iterable loadEventListeners; + private final Iterable refreshEventListeners; + private final Iterable replicateEventListeners; + private final Iterable autoFlushEventListeners; + private final Iterable dirtyCheckEventListeners; + private final Iterable flushEventListeners; + private final Iterable initCollectionEventListeners; + private final Iterable resolveNaturalIdEventListeners; + + FastSessionServices(SessionFactoryImpl sf) { + Objects.requireNonNull( sf ); + final ServiceRegistryImplementor sr = sf.getServiceRegistry(); + final EventListenerRegistry eventListenerRegistry = sr.getService( EventListenerRegistry.class ); + this.clearEventListeners = listeners( eventListenerRegistry, EventType.CLEAR ); + this.saveUpdateEventListeners = listeners( eventListenerRegistry, EventType.SAVE_UPDATE ); + this.evictEventListeners = listeners( eventListenerRegistry, EventType.EVICT ); + this.saveEventListeners = listeners( eventListenerRegistry, EventType.SAVE ); + this.updateEventListeners = listeners( eventListenerRegistry, EventType.UPDATE ); + this.lockEventListeners = listeners( eventListenerRegistry, EventType.LOCK ); + this.persistEventListeners = listeners( eventListenerRegistry, EventType.PERSIST ); + this.persistOnFlushEventListeners = listeners( eventListenerRegistry, EventType.PERSIST_ONFLUSH ); + this.mergeEventListeners = listeners( eventListenerRegistry, EventType.MERGE ); + this.deleteEventListeners = listeners( eventListenerRegistry, EventType.DELETE ); + this.loadEventListeners = listeners( eventListenerRegistry, EventType.LOAD ); + this.refreshEventListeners = listeners( eventListenerRegistry, EventType.REFRESH ); + this.replicateEventListeners = listeners( eventListenerRegistry, EventType.REPLICATE ); + this.autoFlushEventListeners = listeners( eventListenerRegistry, EventType.AUTO_FLUSH ); + this.dirtyCheckEventListeners = listeners( eventListenerRegistry, EventType.DIRTY_CHECK ); + this.flushEventListeners = listeners( eventListenerRegistry, EventType.FLUSH ); + this.initCollectionEventListeners = listeners( eventListenerRegistry, EventType.INIT_COLLECTION ); + this.resolveNaturalIdEventListeners = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID ); + } + + Iterable getClearEventListeners() { + return clearEventListeners; + } + + Iterable getEvictEventListeners() { + return this.evictEventListeners; + } + + Iterable getDirtyCheckEventListeners() { + return this.dirtyCheckEventListeners; + } + + Iterable getSaveUpdateEventListeners() { + return this.saveUpdateEventListeners; + } + + Iterable getSaveEventListeners() { + return this.saveEventListeners; + } + + Iterable getUpdateEventListeners() { + return this.updateEventListeners; + } + + Iterable getLockEventListeners() { + return this.lockEventListeners; + } + + Iterable getPersistEventListeners() { + return persistEventListeners; + } + + Iterable getFlushEventListeners() { + return flushEventListeners; + } + + Iterable getPersistOnFlushEventListeners() { + return persistOnFlushEventListeners; + } + + Iterable getInitCollectionEventListeners() { + return initCollectionEventListeners; + } + + Iterable getLoadEventListeners() { + return loadEventListeners; + } + + Iterable getMergeEventListeners() { + return mergeEventListeners; + } + + Iterable getRefreshEventListeners() { + return refreshEventListeners; + } + + Iterable getDeleteEventListeners() { + return deleteEventListeners; + } + + Iterable getAutoFlushEventListeners() { + return autoFlushEventListeners; + } + + Iterable getReplicateEventListeners() { + return replicateEventListeners; + } + + Iterable getResolveNaturalIdEventListeners() { + return resolveNaturalIdEventListeners; + } + + private static Iterable listeners(EventListenerRegistry elr, EventType type) { + return elr.getEventListenerGroup( type ).listeners(); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index d311b4d18a..5af883fcf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -197,6 +197,8 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { private final transient TypeHelper typeHelper; + private final transient FastSessionServices fastSessionServices; + public SessionFactoryImpl( final MetadataImplementor metadata, SessionFactoryOptions options) { @@ -375,6 +377,8 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { fetchProfiles.put( fetchProfile.getName(), fetchProfile ); } + this.fastSessionServices = new FastSessionServices( this ); + this.observer.sessionFactoryCreated( this ); SessionFactoryRegistry.INSTANCE.addSessionFactory( @@ -1645,4 +1649,12 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { LOG.emptyCompositesEnabled(); } } + + /** + * @return the FastSessionServices for this SessionFactory. + */ + FastSessionServices getFastSessionServices() { + return this.fastSessionServices; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index f7b17e2bcb..f2e1e0db2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -96,8 +96,6 @@ import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.TypedValue; import org.hibernate.engine.transaction.spi.TransactionImplementor; import org.hibernate.engine.transaction.spi.TransactionObserver; -import org.hibernate.event.service.spi.EventListenerGroup; -import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEvent; import org.hibernate.event.spi.AutoFlushEventListener; import org.hibernate.event.spi.ClearEvent; @@ -107,7 +105,6 @@ import org.hibernate.event.spi.DeleteEventListener; import org.hibernate.event.spi.DirtyCheckEvent; import org.hibernate.event.spi.DirtyCheckEventListener; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.EvictEvent; import org.hibernate.event.spi.EvictEventListener; import org.hibernate.event.spi.FlushEvent; @@ -234,7 +231,6 @@ public final class SessionImpl private transient boolean discardOnClose; private transient TransactionObserver transactionObserver; - private transient EventListenerRegistry eventListenerRegistry; public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super( factory, options ); @@ -355,7 +351,7 @@ public final class SessionImpl actionQueue.clear(); final ClearEvent event = new ClearEvent( this ); - for ( ClearEventListener listener : listeners( EventType.CLEAR ) ) { + for ( ClearEventListener listener : fastSessionServices.getClearEventListeners() ) { listener.onClear( event ); } } @@ -633,24 +629,12 @@ public final class SessionImpl checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : listeners( EventType.SAVE_UPDATE ) ) { + for ( SaveOrUpdateEventListener listener : fastSessionServices.getSaveUpdateEventListeners() ) { listener.onSaveOrUpdate( event ); } checkNoUnresolvedActionsAfterOperation(); } - private Iterable listeners(EventType type) { - return eventListenerGroup( type ).listeners(); - } - - private EventListenerGroup eventListenerGroup(EventType type) { - if ( this.eventListenerRegistry == null ) { - this.eventListenerRegistry = getFactory().getServiceRegistry().getService( EventListenerRegistry.class ); - } - return eventListenerRegistry.getEventListenerGroup( type ); - } - - // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override @@ -667,7 +651,7 @@ public final class SessionImpl checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : listeners( EventType.SAVE ) ) { + for ( SaveOrUpdateEventListener listener : fastSessionServices.getSaveEventListeners() ) { listener.onSaveOrUpdate( event ); } checkNoUnresolvedActionsAfterOperation(); @@ -691,7 +675,7 @@ public final class SessionImpl checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : listeners( EventType.UPDATE ) ) { + for ( SaveOrUpdateEventListener listener : fastSessionServices.getUpdateEventListeners() ) { listener.onSaveOrUpdate( event ); } checkNoUnresolvedActionsAfterOperation(); @@ -726,7 +710,7 @@ public final class SessionImpl private void fireLock(LockEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( LockEventListener listener : listeners( EventType.LOCK ) ) { + for ( LockEventListener listener : fastSessionServices.getLockEventListeners() ) { listener.onLock( event ); } delayedAfterCompletion(); @@ -758,7 +742,7 @@ public final class SessionImpl checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( PersistEventListener listener : listeners( EventType.PERSIST ) ) { + for ( PersistEventListener listener : fastSessionServices.getPersistEventListeners() ) { listener.onPersist( event ); } } @@ -782,7 +766,7 @@ public final class SessionImpl pulseTransactionCoordinator(); try { - for ( PersistEventListener listener : listeners( EventType.PERSIST ) ) { + for ( PersistEventListener listener : fastSessionServices.getPersistEventListeners() ) { listener.onPersist( event, copiedAlready ); } } @@ -818,7 +802,7 @@ public final class SessionImpl private void firePersistOnFlush(Map copiedAlready, PersistEvent event) { checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); - for ( PersistEventListener listener : listeners( EventType.PERSIST_ONFLUSH ) ) { + for ( PersistEventListener listener : fastSessionServices.getPersistOnFlushEventListeners() ) { listener.onPersist( event, copiedAlready ); } delayedAfterCompletion(); @@ -828,7 +812,7 @@ public final class SessionImpl checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( PersistEventListener listener : listeners( EventType.PERSIST_ONFLUSH ) ) { + for ( PersistEventListener listener : fastSessionServices.getPersistOnFlushEventListeners() ) { listener.onPersist( event ); } checkNoUnresolvedActionsAfterOperation(); @@ -859,7 +843,7 @@ public final class SessionImpl try { checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( MergeEventListener listener : listeners( EventType.MERGE ) ) { + for ( MergeEventListener listener : fastSessionServices.getMergeEventListeners() ) { listener.onMerge( event ); } checkNoUnresolvedActionsAfterOperation(); @@ -881,7 +865,7 @@ public final class SessionImpl private void fireMerge(Map copiedAlready, MergeEvent event) { try { pulseTransactionCoordinator(); - for ( MergeEventListener listener : listeners( EventType.MERGE ) ) { + for ( MergeEventListener listener : fastSessionServices.getMergeEventListeners() ) { listener.onMerge( event, copiedAlready ); } } @@ -971,7 +955,7 @@ public final class SessionImpl private void fireDelete(DeleteEvent event) { try{ pulseTransactionCoordinator(); - for ( DeleteEventListener listener : listeners( EventType.DELETE ) ) { + for ( DeleteEventListener listener : fastSessionServices.getDeleteEventListeners() ) { listener.onDelete( event ); } } @@ -993,7 +977,7 @@ public final class SessionImpl private void fireDelete(DeleteEvent event, Set transientEntities) { try{ pulseTransactionCoordinator(); - for ( DeleteEventListener listener : listeners( EventType.DELETE ) ) { + for ( DeleteEventListener listener : fastSessionServices.getDeleteEventListeners() ) { listener.onDelete( event, transientEntities ); } } @@ -1270,7 +1254,7 @@ public final class SessionImpl // it seems they prevent these hot methods from being inlined. private void fireLoadNoChecks(LoadEvent event, LoadType loadType) { pulseTransactionCoordinator(); - for ( LoadEventListener listener : listeners( EventType.LOAD ) ) { + for ( LoadEventListener listener : fastSessionServices.getLoadEventListeners() ) { listener.onLoad( event, loadType ); } } @@ -1278,7 +1262,7 @@ public final class SessionImpl private void fireResolveNaturalId(ResolveNaturalIdEvent event) { checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); - for ( ResolveNaturalIdEventListener listener : listeners( EventType.RESOLVE_NATURAL_ID ) ) { + for ( ResolveNaturalIdEventListener listener : fastSessionServices.getResolveNaturalIdEventListeners() ) { listener.onResolveNaturalId( event ); } delayedAfterCompletion(); @@ -1338,7 +1322,7 @@ public final class SessionImpl } } pulseTransactionCoordinator(); - for ( RefreshEventListener listener : listeners( EventType.REFRESH ) ) { + for ( RefreshEventListener listener : fastSessionServices.getRefreshEventListeners() ) { listener.onRefresh( event ); } } @@ -1359,7 +1343,7 @@ public final class SessionImpl private void fireRefresh(Map refreshedAlready, RefreshEvent event) { try { pulseTransactionCoordinator(); - for ( RefreshEventListener listener : listeners( EventType.REFRESH ) ) { + for ( RefreshEventListener listener : fastSessionServices.getRefreshEventListeners() ) { listener.onRefresh( event, refreshedAlready ); } } @@ -1388,7 +1372,7 @@ public final class SessionImpl private void fireReplicate(ReplicateEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( ReplicateEventListener listener : listeners( EventType.REPLICATE ) ) { + for ( ReplicateEventListener listener : fastSessionServices.getReplicateEventListeners() ) { listener.onReplicate( event ); } delayedAfterCompletion(); @@ -1409,7 +1393,7 @@ public final class SessionImpl private void fireEvict(EvictEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( EvictEventListener listener : listeners( EventType.EVICT ) ) { + for ( EvictEventListener listener : fastSessionServices.getEvictEventListeners() ) { listener.onEvict( event ); } delayedAfterCompletion(); @@ -1426,7 +1410,7 @@ public final class SessionImpl return false; } AutoFlushEvent event = new AutoFlushEvent( querySpaces, this ); - for ( AutoFlushEventListener listener : listeners( EventType.AUTO_FLUSH ) ) { + for ( AutoFlushEventListener listener : fastSessionServices.getAutoFlushEventListeners() ) { listener.onAutoFlush( event ); } return event.isFlushRequired(); @@ -1442,7 +1426,7 @@ public final class SessionImpl return true; } DirtyCheckEvent event = new DirtyCheckEvent( this ); - for ( DirtyCheckEventListener listener : listeners( EventType.DIRTY_CHECK ) ) { + for ( DirtyCheckEventListener listener : fastSessionServices.getDirtyCheckEventListeners() ) { listener.onDirtyCheck( event ); } delayedAfterCompletion(); @@ -1465,7 +1449,7 @@ public final class SessionImpl } FlushEvent flushEvent = new FlushEvent( this ); - for ( FlushEventListener listener : listeners( EventType.FLUSH ) ) { + for ( FlushEventListener listener : fastSessionServices.getFlushEventListeners() ) { listener.onFlush( flushEvent ); } @@ -2269,7 +2253,7 @@ public final class SessionImpl checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); InitializeCollectionEvent event = new InitializeCollectionEvent( collection, this ); - for ( InitializeCollectionEventListener listener : listeners( EventType.INIT_COLLECTION ) ) { + for ( InitializeCollectionEventListener listener : fastSessionServices.getInitCollectionEventListeners() ) { listener.onInitializeCollection( event ); } delayedAfterCompletion(); From a39acebde84e9d2a16d5b8472fc0b4430ed0bfbd Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 9 Aug 2019 12:55:28 +0100 Subject: [PATCH 46/99] HHH-13565 Move field AbstractSharedSessionContract#disallowOutOfTransactionUpdateOperations to constants in FastSessionServices --- .../internal/AbstractSharedSessionContract.java | 6 ++---- .../org/hibernate/internal/FastSessionServices.java | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 4cc33fa7ca..ca2da2c541 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -139,7 +139,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont protected boolean closed; protected boolean waitingForAutoClose; - private transient boolean disallowOutOfTransactionUpdateOperations; // transient & non-final for Serialization purposes - ugh private transient SessionEventListenerManagerImpl sessionEventsManager = new SessionEventListenerManagerImpl(); @@ -157,7 +156,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont this.factory = factory; this.fastSessionServices = factory.getFastSessionServices(); this.cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); - this.disallowOutOfTransactionUpdateOperations = !factory.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); + this.flushMode = options.getInitialSessionFlushMode(); @@ -407,7 +406,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { - if ( disallowOutOfTransactionUpdateOperations && !isTransactionInProgress() ) { + if ( fastSessionServices.disallowOutOfTransactionUpdateOperations && !isTransactionInProgress() ) { throw new TransactionRequiredException( exceptionMessage ); } } @@ -1239,7 +1238,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont .buildTransactionCoordinator( jdbcCoordinator, this ); entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor ); - this.disallowOutOfTransactionUpdateOperations = !getFactory().getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 020d05c7e1..4e2139a9bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -49,6 +49,8 @@ import java.util.Objects; */ final class FastSessionServices { + // All session events need to be iterated frequently: + private final Iterable clearEventListeners; private final Iterable saveUpdateEventListeners; private final Iterable evictEventListeners; @@ -68,9 +70,14 @@ final class FastSessionServices { private final Iterable initCollectionEventListeners; private final Iterable resolveNaturalIdEventListeners; + //Intentionally Package private: + final boolean disallowOutOfTransactionUpdateOperations; + FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); final ServiceRegistryImplementor sr = sf.getServiceRegistry(); + + // Pre-compute all iterators on Event listeners: final EventListenerRegistry eventListenerRegistry = sr.getService( EventListenerRegistry.class ); this.clearEventListeners = listeners( eventListenerRegistry, EventType.CLEAR ); this.saveUpdateEventListeners = listeners( eventListenerRegistry, EventType.SAVE_UPDATE ); @@ -90,6 +97,9 @@ final class FastSessionServices { this.flushEventListeners = listeners( eventListenerRegistry, EventType.FLUSH ); this.initCollectionEventListeners = listeners( eventListenerRegistry, EventType.INIT_COLLECTION ); this.resolveNaturalIdEventListeners = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID ); + + //Other highly useful constants: + this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); } Iterable getClearEventListeners() { From 8a3c1ee5049c5a0c34836c35da6911d333393e8a Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 9 Aug 2019 13:00:49 +0100 Subject: [PATCH 47/99] HHH-13565 Promote field useStreamForLobBinding as cross-Session constant --- .../hibernate/internal/AbstractSharedSessionContract.java | 8 +------- .../java/org/hibernate/internal/FastSessionServices.java | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index ca2da2c541..0e878e8f79 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -37,7 +37,6 @@ import org.hibernate.Transaction; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.cache.spi.CacheTransactionSynchronization; -import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; @@ -143,7 +142,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont // transient & non-final for Serialization purposes - ugh private transient SessionEventListenerManagerImpl sessionEventsManager = new SessionEventListenerManagerImpl(); private transient EntityNameResolver entityNameResolver; - private transient Boolean useStreamForLobBinding; private Integer jdbcBatchSize; @@ -537,11 +535,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public boolean useStreamForLobBinding() { - if ( useStreamForLobBinding == null ) { - useStreamForLobBinding = Environment.useStreamsForBinary() - || getJdbcServices().getJdbcEnvironment().getDialect().useInputStreamToInsertBlob(); - } - return useStreamForLobBinding; + return fastSessionServices.useStreamForLobBinding; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 4e2139a9bd..2fa489166a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -6,6 +6,8 @@ */ package org.hibernate.internal; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEventListener; import org.hibernate.event.spi.ClearEventListener; @@ -72,10 +74,12 @@ final class FastSessionServices { //Intentionally Package private: final boolean disallowOutOfTransactionUpdateOperations; + final boolean useStreamForLobBinding; FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); final ServiceRegistryImplementor sr = sf.getServiceRegistry(); + final JdbcServices jdbcServices = sf.getJdbcServices(); // Pre-compute all iterators on Event listeners: final EventListenerRegistry eventListenerRegistry = sr.getService( EventListenerRegistry.class ); @@ -100,6 +104,8 @@ final class FastSessionServices { //Other highly useful constants: this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); + this.useStreamForLobBinding = Environment.useStreamsForBinary() + || jdbcServices.getJdbcEnvironment().getDialect().useInputStreamToInsertBlob(); } Iterable getClearEventListeners() { From 72ce71ef1079d183c5f6d1b67c65f984f67d25b4 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 9 Aug 2019 13:05:17 +0100 Subject: [PATCH 48/99] HHH-13565 Dialect is another constant --- .../internal/AbstractSharedSessionContract.java | 9 +-------- .../hibernate/internal/FastSessionServices.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 0e878e8f79..3a92f8d5fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -37,7 +37,6 @@ import org.hibernate.Transaction; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.cache.spi.CacheTransactionSynchronization; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; import org.hibernate.engine.jdbc.LobCreationContext; @@ -562,13 +561,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { - if ( !sqlTypeDescriptor.canBeRemapped() ) { - return sqlTypeDescriptor; - } - - final Dialect dialect = getJdbcServices().getJdbcEnvironment().getDialect(); - final SqlTypeDescriptor remapped = dialect.remapSqlTypeDescriptor( sqlTypeDescriptor ); - return remapped == null ? sqlTypeDescriptor : remapped; + return fastSessionServices.remapSqlTypeDescriptor( sqlTypeDescriptor ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 2fa489166a..e7c7afe97d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -7,6 +7,7 @@ package org.hibernate.internal; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEventListener; @@ -26,6 +27,7 @@ import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.event.spi.ResolveNaturalIdEventListener; import org.hibernate.event.spi.SaveOrUpdateEventListener; import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import java.util.Objects; @@ -76,6 +78,8 @@ final class FastSessionServices { final boolean disallowOutOfTransactionUpdateOperations; final boolean useStreamForLobBinding; + private final Dialect dialect; + FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); final ServiceRegistryImplementor sr = sf.getServiceRegistry(); @@ -103,9 +107,10 @@ final class FastSessionServices { this.resolveNaturalIdEventListeners = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID ); //Other highly useful constants: + this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); this.useStreamForLobBinding = Environment.useStreamsForBinary() - || jdbcServices.getJdbcEnvironment().getDialect().useInputStreamToInsertBlob(); + || dialect.useInputStreamToInsertBlob(); } Iterable getClearEventListeners() { @@ -184,4 +189,13 @@ final class FastSessionServices { return elr.getEventListenerGroup( type ).listeners(); } + SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { + if ( !sqlTypeDescriptor.canBeRemapped() ) { + return sqlTypeDescriptor; + } + + final SqlTypeDescriptor remapped = dialect.remapSqlTypeDescriptor( sqlTypeDescriptor ); + return remapped == null ? sqlTypeDescriptor : remapped; + } + } From 185ef2edb7cae1c74f0f92ba91b79ef2b35e2042 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 9 Aug 2019 13:19:11 +0100 Subject: [PATCH 49/99] HHH-13565 Extract some hot ServiceRegistry services as well --- .../internal/AbstractSharedSessionContract.java | 13 +++++-------- .../hibernate/internal/FastSessionServices.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 3a92f8d5fd..7384264360 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -210,11 +210,8 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont else { this.isTransactionCoordinatorShared = false; this.autoJoinTransactions = options.shouldAutoJoinTransactions(); - this.jdbcCoordinator = new JdbcCoordinatorImpl( options.getConnection(), this ); - this.transactionCoordinator = factory.getServiceRegistry() - .getService( TransactionCoordinatorBuilder.class ) - .buildTransactionCoordinator( jdbcCoordinator, this ); + this.transactionCoordinator = fastSessionServices.transactionCoordinatorBuilder.buildTransactionCoordinator( jdbcCoordinator, this ); } } @@ -510,17 +507,17 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont public JdbcConnectionAccess getJdbcConnectionAccess() { // See class-level JavaDocs for a discussion of the concurrent-access safety of this method if ( jdbcConnectionAccess == null ) { - if ( !factory.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) { + if ( ! fastSessionServices.requiresMultiTenantConnectionProvider ) { jdbcConnectionAccess = new NonContextualJdbcConnectionAccess( getEventListenerManager(), - factory.getServiceRegistry().getService( ConnectionProvider.class ) + fastSessionServices.connectionProvider ); } else { jdbcConnectionAccess = new ContextualJdbcConnectionAccess( getTenantIdentifier(), getEventListenerManager(), - factory.getServiceRegistry().getService( MultiTenantConnectionProvider.class ) + fastSessionServices.multiTenantConnectionProvider ); } } @@ -964,7 +961,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont final Class actualReturnedClass; final String entityClassName = ( (NativeSQLQueryRootReturn) nativeSQLQueryReturn ).getReturnEntityName(); try { - actualReturnedClass = getFactory().getServiceRegistry().getService( ClassLoaderService.class ).classForName( entityClassName ); + actualReturnedClass = fastSessionServices.classLoaderService.classForName( entityClassName ); } catch ( ClassLoadingException e ) { throw new AssertionFailure( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index e7c7afe97d..bdda279fd7 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -6,8 +6,11 @@ */ package org.hibernate.internal; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEventListener; @@ -26,6 +29,7 @@ import org.hibernate.event.spi.RefreshEventListener; import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.event.spi.ResolveNaturalIdEventListener; import org.hibernate.event.spi.SaveOrUpdateEventListener; +import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; @@ -77,7 +81,13 @@ final class FastSessionServices { //Intentionally Package private: final boolean disallowOutOfTransactionUpdateOperations; final boolean useStreamForLobBinding; + final boolean requiresMultiTenantConnectionProvider; + final ConnectionProvider connectionProvider; + final MultiTenantConnectionProvider multiTenantConnectionProvider; + final ClassLoaderService classLoaderService; + final TransactionCoordinatorBuilder transactionCoordinatorBuilder; + //Private fields: private final Dialect dialect; FastSessionServices(SessionFactoryImpl sf) { @@ -111,6 +121,13 @@ final class FastSessionServices { this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob(); + this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); + + //Some "hot" services: + this.connectionProvider = requiresMultiTenantConnectionProvider ? null : sr.getService( ConnectionProvider.class ); + this.multiTenantConnectionProvider = requiresMultiTenantConnectionProvider ? sr.getService( MultiTenantConnectionProvider.class ) : null; + this.classLoaderService = sr.getService( ClassLoaderService.class ); + this.transactionCoordinatorBuilder = sr.getService( TransactionCoordinatorBuilder.class ); } Iterable getClearEventListeners() { From 8931ef0962a026a9dad7dd72414137135bc3ce7e Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 12 Aug 2019 09:41:49 +0100 Subject: [PATCH 50/99] HHH-13565 Promote JDBCServices as hot service to be retrieved from FastSessionService as well --- .../jdbc/internal/JdbcCoordinatorImpl.java | 10 +++++----- .../internal/AbstractSharedSessionContract.java | 2 +- .../hibernate/internal/FastSessionServices.java | 2 ++ .../internal/LogicalConnectionManagedImpl.java | 17 ++++++++--------- .../jdbc/internal/JdbcCoordinatorTest.java | 5 ++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 0d5dd3876b..0101585b92 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -82,7 +82,8 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { */ public JdbcCoordinatorImpl( Connection userSuppliedConnection, - JdbcSessionOwner owner) { + JdbcSessionOwner owner, + JdbcServices jdbcServices) { this.isUserSuppliedConnection = userSuppliedConnection != null; final ResourceRegistry resourceRegistry = new ResourceRegistryStandardImpl( @@ -95,13 +96,12 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { this.logicalConnection = new LogicalConnectionManagedImpl( owner.getJdbcConnectionAccess(), owner.getJdbcSessionContext(), - resourceRegistry + resourceRegistry, + jdbcServices ); } this.owner = owner; - this.jdbcServices = owner.getJdbcSessionContext() - .getServiceRegistry() - .getService( JdbcServices.class ); + this.jdbcServices = jdbcServices; } private JdbcCoordinatorImpl( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 7384264360..847105cbb6 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -210,7 +210,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont else { this.isTransactionCoordinatorShared = false; this.autoJoinTransactions = options.shouldAutoJoinTransactions(); - this.jdbcCoordinator = new JdbcCoordinatorImpl( options.getConnection(), this ); + this.jdbcCoordinator = new JdbcCoordinatorImpl( options.getConnection(), this, fastSessionServices.jdbcServices ); this.transactionCoordinator = fastSessionServices.transactionCoordinatorBuilder.buildTransactionCoordinator( jdbcCoordinator, this ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index bdda279fd7..7a42c4f82c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -86,6 +86,7 @@ final class FastSessionServices { final MultiTenantConnectionProvider multiTenantConnectionProvider; final ClassLoaderService classLoaderService; final TransactionCoordinatorBuilder transactionCoordinatorBuilder; + final JdbcServices jdbcServices; //Private fields: private final Dialect dialect; @@ -128,6 +129,7 @@ final class FastSessionServices { this.multiTenantConnectionProvider = requiresMultiTenantConnectionProvider ? sr.getService( MultiTenantConnectionProvider.class ) : null; this.classLoaderService = sr.getService( ClassLoaderService.class ); this.transactionCoordinatorBuilder = sr.getService( TransactionCoordinatorBuilder.class ); + this.jdbcServices = sr.getService( JdbcServices.class ); } Iterable getClearEventListeners() { diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java index 18b5774ece..2a4cb84d5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java @@ -48,20 +48,17 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple public LogicalConnectionManagedImpl( JdbcConnectionAccess jdbcConnectionAccess, JdbcSessionContext jdbcSessionContext, - ResourceRegistry resourceRegistry) { + ResourceRegistry resourceRegistry, + JdbcServices jdbcServices) { this.jdbcConnectionAccess = jdbcConnectionAccess; this.observer = jdbcSessionContext.getObserver(); this.resourceRegistry = resourceRegistry; this.connectionHandlingMode = determineConnectionHandlingMode( jdbcSessionContext.getPhysicalConnectionHandlingMode(), - jdbcConnectionAccess + jdbcConnectionAccess ); - ); - - this.sqlExceptionHelper = jdbcSessionContext.getServiceRegistry() - .getService( JdbcServices.class ) - .getSqlExceptionHelper(); + this.sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); if ( connectionHandlingMode.getAcquisitionMode() == ConnectionAcquisitionMode.IMMEDIATELY ) { acquireConnectionIfNeeded(); @@ -94,7 +91,9 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple JdbcConnectionAccess jdbcConnectionAccess, JdbcSessionContext jdbcSessionContext, boolean closed) { - this( jdbcConnectionAccess, jdbcSessionContext, new ResourceRegistryStandardImpl() ); + this( jdbcConnectionAccess, jdbcSessionContext, new ResourceRegistryStandardImpl(), + jdbcSessionContext.getServiceRegistry().getService( JdbcServices.class ) + ); this.closed = closed; } @@ -221,7 +220,7 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple public static LogicalConnectionManagedImpl deserialize( ObjectInputStream ois, JdbcConnectionAccess jdbcConnectionAccess, - JdbcSessionContext jdbcSessionContext) throws IOException, ClassNotFoundException { + JdbcSessionContext jdbcSessionContext) throws IOException { final boolean isClosed = ois.readBoolean(); return new LogicalConnectionManagedImpl( jdbcConnectionAccess, jdbcSessionContext, isClosed ); } diff --git a/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java b/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java index df1646aa58..50a8334265 100644 --- a/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorTest.java @@ -62,8 +62,6 @@ public class JdbcCoordinatorTest { when( sessionContext.getObserver() ).thenReturn( jdbcObserver ); JdbcServices jdbcServices = Mockito.mock( JdbcServices.class ); - when( serviceRegistry.getService( eq( JdbcServices.class ) ) ).thenReturn( - jdbcServices ); ConfigurationService configurationService = Mockito.mock( ConfigurationService.class ); when( serviceRegistry.getService( eq( ConfigurationService.class ) ) ).thenReturn( @@ -77,7 +75,8 @@ public class JdbcCoordinatorTest { JdbcCoordinatorImpl jdbcCoordinator = new JdbcCoordinatorImpl( null, - sessionOwner + sessionOwner, + jdbcServices ); Batch currentBatch = Mockito.mock( Batch.class ); From be9fe275923c171390ee8ddd340b35a58e7baa73 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 12 Aug 2019 19:47:26 +0100 Subject: [PATCH 51/99] HHH-13565 Promote to FastSessionServices: isJtaTransactionAccessible --- .../AbstractSharedSessionContract.java | 13 +------------ .../internal/FastSessionServices.java | 19 ++++++++++++++++--- .../org/hibernate/internal/SessionImpl.java | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 847105cbb6..6f1d201cba 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -407,7 +407,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public Transaction getTransaction() throws HibernateException { - if ( !isTransactionAccessible() ) { + if ( ! fastSessionServices.isJtaTransactionAccessible ) { throw new IllegalStateException( "Transaction is not accessible when using JTA with JPA-compliant transaction access enabled" ); @@ -415,17 +415,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont return accessTransaction(); } - protected boolean isTransactionAccessible() { - // JPA requires that access not be provided to the transaction when using JTA. - // This is overridden when SessionFactoryOptions isJtaTransactionAccessEnabled() is true. - if ( getFactory().getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() && - getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta() && - !getFactory().getSessionFactoryOptions().isJtaTransactionAccessEnabled() ) { - return false; - } - return true; - } - @Override public Transaction accessTransaction() { if ( this.currentHibernateTransaction == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 7a42c4f82c..d593245b32 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -87,6 +87,7 @@ final class FastSessionServices { final ClassLoaderService classLoaderService; final TransactionCoordinatorBuilder transactionCoordinatorBuilder; final JdbcServices jdbcServices; + final boolean isJtaTransactionAccessible; //Private fields: private final Dialect dialect; @@ -120,9 +121,8 @@ final class FastSessionServices { //Other highly useful constants: this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); - this.useStreamForLobBinding = Environment.useStreamsForBinary() - || dialect.useInputStreamToInsertBlob(); - this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); + this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob(); + this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); //Some "hot" services: this.connectionProvider = requiresMultiTenantConnectionProvider ? null : sr.getService( ConnectionProvider.class ); @@ -130,6 +130,8 @@ final class FastSessionServices { this.classLoaderService = sr.getService( ClassLoaderService.class ); this.transactionCoordinatorBuilder = sr.getService( TransactionCoordinatorBuilder.class ); this.jdbcServices = sr.getService( JdbcServices.class ); + + this.isJtaTransactionAccessible = isTransactionAccessible( sf, transactionCoordinatorBuilder ); } Iterable getClearEventListeners() { @@ -217,4 +219,15 @@ final class FastSessionServices { return remapped == null ? sqlTypeDescriptor : remapped; } + private static boolean isTransactionAccessible(SessionFactoryImpl sf, TransactionCoordinatorBuilder transactionCoordinatorBuilder) { + // JPA requires that access not be provided to the transaction when using JTA. + // This is overridden when SessionFactoryOptions isJtaTransactionAccessEnabled() is true. + if ( sf.getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() && + transactionCoordinatorBuilder.isJta() && + !sf.getSessionFactoryOptions().isJtaTransactionAccessEnabled() ) { + return false; + } + return true; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index f2e1e0db2c..82603aaf35 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -2493,7 +2493,7 @@ public final class SessionImpl // We do not want an exception to be thrown if the transaction // is not accessible. If the transaction is not accessible, // then return null. - return isTransactionAccessible() ? accessTransaction() : null; + return fastSessionServices.isJtaTransactionAccessible ? accessTransaction() : null; } @Override From bc5b3c028febed445fe1f1e70eaf874e41dc93cc Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 12 Aug 2019 10:20:33 +0100 Subject: [PATCH 52/99] HHH-13565 Remove some related dead code --- .../internal/StatefulPersistenceContext.java | 6 +---- .../internal/SessionFactoryImpl.java | 1 - .../org/hibernate/internal/SessionImpl.java | 25 ------------------- .../internal/AbstractLoadPlanBasedLoader.java | 9 ------- .../internal/ResultSetProcessorImpl.java | 4 --- 5 files changed, 1 insertion(+), 44 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index dabcc510c6..93f7d9cc4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -101,7 +101,6 @@ public class StatefulPersistenceContext implements PersistenceContext { private Map entitiesByUniqueKey; private EntityEntryContext entityEntryContext; -// private Map entityEntries; // Entity proxies, by EntityKey private ConcurrentMap proxiesByKey; @@ -295,11 +294,8 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void afterTransactionCompletion() { cleanUpInsertedKeysAfterTransaction(); + // Downgrade locks entityEntryContext.downgradeLocks(); -// // Downgrade locks -// for ( EntityEntry o : entityEntries.values() ) { -// o.setLockMode( LockMode.NONE ); -// } } /** diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 5af883fcf4..af25ea4876 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -1160,7 +1160,6 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { //todo : expose setting private SessionOwnerBehavior sessionOwnerBehavior = SessionOwnerBehavior.LEGACY_NATIVE; - private PersistenceUnitTransactionType persistenceUnitTransactionType; SessionBuilderImpl(SessionFactoryImpl sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 82603aaf35..b17a51ebdc 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -167,8 +167,6 @@ import org.hibernate.query.internal.CollectionFilterImpl; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.resource.transaction.TransactionRequiredForJoinException; import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl; -import org.hibernate.resource.transaction.backend.jta.internal.synchronization.AfterCompletionAction; -import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ManagedFlushChecker; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.resource.transaction.spi.TransactionStatus; import org.hibernate.stat.SessionStatistics; @@ -456,10 +454,6 @@ public final class SessionImpl } } - private boolean isFlushModeNever() { - return FlushMode.isManualFlushMode( getHibernateFlushMode() ); - } - private void managedFlush() { if ( isClosed() && !waitingForAutoClose ) { log.trace( "Skipping auto-flush due to session closed" ); @@ -3353,25 +3347,6 @@ public final class SessionImpl return getHibernateFlushMode() != FlushMode.MANUAL; } - private static final AfterCompletionAction STANDARD_AFTER_COMPLETION_ACTION = (AfterCompletionAction) (successful, session) -> { - // nothing to do by default. - }; - - - public static class ManagedFlushCheckerStandardImpl implements ManagedFlushChecker { - @Override - public boolean shouldDoManagedFlush(SessionImplementor session) { - if ( session.isClosed() ) { - return false; - } - return session.getHibernateFlushMode() != FlushMode.MANUAL; - } - } - - private static final ManagedFlushCheckerStandardImpl STANDARD_MANAGED_FLUSH_CHECKER = new ManagedFlushCheckerStandardImpl() { - }; - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HibernateEntityManager impl diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/AbstractLoadPlanBasedLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/AbstractLoadPlanBasedLoader.java index eb5a52688a..eb320df4c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/AbstractLoadPlanBasedLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/AbstractLoadPlanBasedLoader.java @@ -11,7 +11,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -38,7 +37,6 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; -import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.resource.jdbc.ResourceRegistry; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.Type; @@ -138,13 +136,6 @@ public abstract class AbstractLoadPlanBasedLoader { } } - protected SqlStatementWrapper executeQueryStatement( - final QueryParameters queryParameters, - final boolean scroll, - final SharedSessionContractImplementor session) throws SQLException { - return executeQueryStatement( getStaticLoadQuery().getSqlStatement(), queryParameters, scroll, session ); - } - protected SqlStatementWrapper executeQueryStatement( String sqlStatement, QueryParameters queryParameters, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java index 886ce50149..c710b2e01c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java @@ -63,10 +63,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { this.hadSubselectFetches = hadSubselectFetches; } - public RowReader getRowReader() { - return rowReader; - } - @Override public ScrollableResultSetProcessor toOnDemandForm() { // todo : implement From 457e9b61faffc9f5d4fd60fb164ca6b0efa77b71 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 12 Aug 2019 20:28:25 +0100 Subject: [PATCH 53/99] HHH-13565 Formatting improvements --- .../AbstractSharedSessionContract.java | 8 +++---- .../internal/util/ConfigurationHelper.java | 22 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 6f1d201cba..cd5932de2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -423,7 +423,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont this ); } - if ( !isClosed() || (waitingForAutoClose && factory.isOpen()) ) { + if ( !isClosed() || ( waitingForAutoClose && factory.isOpen() ) ) { getTransactionCoordinator().pulse(); } return this.currentHibernateTransaction; @@ -902,7 +902,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @SuppressWarnings({"WeakerAccess", "unchecked"}) protected NativeQueryImplementor createNativeQuery(NamedSQLQueryDefinition queryDefinition, Class resultType) { - if ( resultType != null && !Tuple.class.equals(resultType)) { + if ( resultType != null && !Tuple.class.equals( resultType ) ) { resultClassChecking( resultType, queryDefinition ); } @@ -911,8 +911,8 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont this, factory.getQueryPlanCache().getSQLParameterMetadata( queryDefinition.getQueryString(), false ) ); - if (Tuple.class.equals(resultType)) { - query.setResultTransformer(new NativeQueryTupleTransformer()); + if ( Tuple.class.equals( resultType ) ) { + query.setResultTransformer( new NativeQueryTupleTransformer() ); } query.setHibernateFlushMode( queryDefinition.getFlushMode() ); query.setComment( queryDefinition.getComment() != null ? queryDefinition.getComment() : queryDefinition.getName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/ConfigurationHelper.java b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/ConfigurationHelper.java index 8a66a509c7..ea78f317db 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/ConfigurationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/ConfigurationHelper.java @@ -30,21 +30,21 @@ public abstract class ConfigurationHelper { public static FlushMode getFlushMode(Object value, FlushMode defaultFlushMode) { final FlushMode flushMode; - if (value instanceof FlushMode) { + if ( value instanceof FlushMode ) { flushMode = (FlushMode) value; } - else if (value instanceof javax.persistence.FlushModeType) { - flushMode = ConfigurationHelper.getFlushMode( (javax.persistence.FlushModeType) value); + else if ( value instanceof javax.persistence.FlushModeType ) { + flushMode = ConfigurationHelper.getFlushMode( (javax.persistence.FlushModeType) value ); } - else if (value instanceof String) { - flushMode = ConfigurationHelper.getFlushMode( (String) value); + else if ( value instanceof String ) { + flushMode = ConfigurationHelper.getFlushMode( (String) value ); } else { flushMode = defaultFlushMode; } - if (flushMode == null) { - throw new PersistenceException("Unable to parse org.hibernate.flushMode: " + value); + if ( flushMode == null ) { + throw new PersistenceException( "Unable to parse org.hibernate.flushMode: " + value ); } return flushMode; @@ -55,21 +55,21 @@ public abstract class ConfigurationHelper { } private static FlushMode getFlushMode(String flushMode) { - if (flushMode == null) { + if ( flushMode == null ) { return null; } - flushMode = flushMode.toUpperCase(Locale.ROOT); + flushMode = flushMode.toUpperCase( Locale.ROOT ); return FlushMode.valueOf( flushMode ); } private static FlushMode getFlushMode(FlushModeType flushMode) { - switch(flushMode) { + switch ( flushMode ) { case AUTO: return FlushMode.AUTO; case COMMIT: return FlushMode.COMMIT; default: - throw new AssertionFailure("Unknown FlushModeType: " + flushMode); + throw new AssertionFailure( "Unknown FlushModeType: " + flushMode ); } } From e23e6a73e6a6b068573e4ab4044d40bb78e67e9f Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 12 Aug 2019 21:33:03 +0100 Subject: [PATCH 54/99] HHH-13565 Extract Session properties to avoid eager initialization of Map properties --- .../internal/FastSessionServices.java | 94 +++++++++++++++ .../org/hibernate/internal/SessionImpl.java | 113 +++++++++--------- 2 files changed, 150 insertions(+), 57 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index d593245b32..af03aaca85 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -6,6 +6,9 @@ */ package org.hibernate.internal; +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.LockOptions; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; @@ -29,12 +32,27 @@ import org.hibernate.event.spi.RefreshEventListener; import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.event.spi.ResolveNaturalIdEventListener; import org.hibernate.event.spi.SaveOrUpdateEventListener; +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.QueryHints; +import org.hibernate.jpa.internal.util.CacheModeHelper; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import javax.persistence.CacheRetrieveMode; +import javax.persistence.CacheStoreMode; +import javax.persistence.PessimisticLockScope; + +import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_SCOPE; +import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT; +import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_RETRIEVE_MODE; +import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE; + /** * Internal component. * @@ -57,6 +75,11 @@ import java.util.Objects; */ final class FastSessionServices { + /** + * Default session properties + */ + final Map defaultSessionProperties; + // All session events need to be iterated frequently: private final Iterable clearEventListeners; @@ -88,9 +111,12 @@ final class FastSessionServices { final TransactionCoordinatorBuilder transactionCoordinatorBuilder; final JdbcServices jdbcServices; final boolean isJtaTransactionAccessible; + final CacheMode initialSessionCacheMode; //Private fields: private final Dialect dialect; + private final CacheStoreMode defaultCacheStoreMode; + private final CacheRetrieveMode defaultCacheRetrieveMode; FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); @@ -132,6 +158,11 @@ final class FastSessionServices { this.jdbcServices = sr.getService( JdbcServices.class ); this.isJtaTransactionAccessible = isTransactionAccessible( sf, transactionCoordinatorBuilder ); + + this.defaultSessionProperties = initializeDefaultSessionProperties( sf ); + this.defaultCacheStoreMode = determineCacheStoreMode( defaultSessionProperties ); + this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties ); + this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode ); } Iterable getClearEventListeners() { @@ -230,4 +261,67 @@ final class FastSessionServices { return true; } + private static Map initializeDefaultSessionProperties(SessionFactoryImpl sf) { + HashMap p = new HashMap<>(); + + //Static defaults: + p.putIfAbsent( AvailableSettings.FLUSH_MODE, FlushMode.AUTO.name() ); + p.putIfAbsent( JPA_LOCK_SCOPE, PessimisticLockScope.EXTENDED.name() ); + p.putIfAbsent( JPA_LOCK_TIMEOUT, LockOptions.WAIT_FOREVER ); + p.putIfAbsent( JPA_SHARED_CACHE_RETRIEVE_MODE, CacheModeHelper.DEFAULT_RETRIEVE_MODE ); + p.putIfAbsent( JPA_SHARED_CACHE_STORE_MODE, CacheModeHelper.DEFAULT_STORE_MODE ); + + //Defaults defined by SessionFactory configuration: + final String[] ENTITY_MANAGER_SPECIFIC_PROPERTIES = { + JPA_LOCK_SCOPE, + JPA_LOCK_TIMEOUT, + AvailableSettings.FLUSH_MODE, + JPA_SHARED_CACHE_RETRIEVE_MODE, + JPA_SHARED_CACHE_STORE_MODE, + QueryHints.SPEC_HINT_TIMEOUT + }; + final Map properties = sf.getProperties(); + for ( String key : ENTITY_MANAGER_SPECIFIC_PROPERTIES ) { + if ( properties.containsKey( key ) ) { + p.put( key, properties.get( key ) ); + } + } + return Collections.unmodifiableMap( p ); + } + + /** + * @param properties the Session properties + * @return either the CacheStoreMode as defined in the Session specific properties, or as defined in the + * properties shared across all sessions (the defaults). + */ + CacheStoreMode getCacheStoreMode(final Map properties) { + if ( properties == null ) { + return this.defaultCacheStoreMode; + } + else { + return determineCacheStoreMode( properties ); + } + } + + /** + * @param properties the Session properties + * @return either the CacheRetrieveMode as defined in the Session specific properties, or as defined in the + * properties shared across all sessions (the defaults). + */ + CacheRetrieveMode getCacheRetrieveMode(Map properties) { + if ( properties == null ) { + return this.defaultCacheRetrieveMode; + } + else { + return determineCacheRetrieveMode( properties ); + } + } + + private static CacheRetrieveMode determineCacheRetrieveMode(Map settings) { + return ( CacheRetrieveMode ) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE ); + } + + private static CacheStoreMode determineCacheStoreMode(Map settings) { + return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index b17a51ebdc..c7d897b090 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -192,23 +192,15 @@ import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE; * @author Steve Ebersole * @author Brett Meyer * @author Chris Cranford + * @author Sanne Grinovero */ public final class SessionImpl extends AbstractSessionImpl implements EventSource, SessionImplementor, HibernateEntityManagerImplementor { private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( SessionImpl.class ); - - private static final String[] ENTITY_MANAGER_SPECIFIC_PROPERTIES = { - JPA_LOCK_SCOPE, - JPA_LOCK_TIMEOUT, - AvailableSettings.FLUSH_MODE, - JPA_SHARED_CACHE_RETRIEVE_MODE, - JPA_SHARED_CACHE_STORE_MODE, - QueryHints.SPEC_HINT_TIMEOUT - }; - - private Map properties = new HashMap<>(); + // Defaults to null which means the properties are the default - as defined in FastSessionServices#defaultSessionProperties + private Map properties; private transient ActionQueue actionQueue; private transient StatefulPersistenceContext persistenceContext; @@ -260,67 +252,38 @@ public final class SessionImpl statistics.openSession(); } + setLockOptions( this.properties == null ? fastSessionServices.defaultSessionProperties : this.properties, this.lockOptions ); + getSession().setCacheMode( fastSessionServices.initialSessionCacheMode ); + // NOTE : pulse() already handles auto-join-ability correctly getTransactionCoordinator().pulse(); - setDefaultProperties(); - applyProperties(); - if ( log.isTraceEnabled() ) { log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), getTimestamp() ); } } - private void setDefaultProperties() { - properties.putIfAbsent( AvailableSettings.FLUSH_MODE, getHibernateFlushMode().name() ); - properties.putIfAbsent( JPA_LOCK_SCOPE, PessimisticLockScope.EXTENDED.name() ); - properties.putIfAbsent( JPA_LOCK_TIMEOUT, LockOptions.WAIT_FOREVER ); - properties.putIfAbsent( JPA_SHARED_CACHE_RETRIEVE_MODE, CacheModeHelper.DEFAULT_RETRIEVE_MODE ); - properties.putIfAbsent( JPA_SHARED_CACHE_STORE_MODE, CacheModeHelper.DEFAULT_STORE_MODE ); - } - - - private void applyProperties() { - applyEntityManagerSpecificProperties(); - setHibernateFlushMode( ConfigurationHelper.getFlushMode( properties.get( AvailableSettings.FLUSH_MODE ), FlushMode.AUTO ) ); - setLockOptions( this.properties, this.lockOptions ); - getSession().setCacheMode( - CacheModeHelper.interpretCacheMode( - currentCacheStoreMode(), - currentCacheRetrieveMode() - ) - ); - } - - private void applyEntityManagerSpecificProperties() { - final Map properties = getFactory().getProperties(); - for ( String key : ENTITY_MANAGER_SPECIFIC_PROPERTIES ) { - if ( properties.containsKey( key ) ) { - this.properties.put( key, properties.get( key ) ); - } - } - } - protected void applyQuerySettingsAndHints(Query query) { if ( lockOptions.getLockMode() != LockMode.NONE ) { query.setLockMode( getLockMode( lockOptions.getLockMode() ) ); } final Object queryTimeout; - if ( ( queryTimeout = properties.get( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) { + if ( ( queryTimeout = getSessionProperty( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) { query.setHint( QueryHints.SPEC_HINT_TIMEOUT, queryTimeout ); } final Object lockTimeout; - if ( ( lockTimeout = properties.get( JPA_LOCK_TIMEOUT ) ) != null ) { + if ( ( lockTimeout = getSessionProperty( JPA_LOCK_TIMEOUT ) ) != null ) { query.setHint( JPA_LOCK_TIMEOUT, lockTimeout ); } } - private CacheRetrieveMode currentCacheRetrieveMode() { - return determineCacheRetrieveMode( properties ); - } - - private CacheStoreMode currentCacheStoreMode() { - return determineCacheStoreMode( properties ); + private Object getSessionProperty(final String name) { + if ( properties == null ) { + return fastSessionServices.defaultSessionProperties.get( name ); + } + else { + return properties.get( name ); + } } @Override @@ -3519,20 +3482,20 @@ public final class SessionImpl } if ( retrieveMode == null ) { // use the EM setting - retrieveMode = determineCacheRetrieveMode( this.properties ); + retrieveMode = fastSessionServices.getCacheRetrieveMode( this.properties ); } if ( storeMode == null ) { // use the EM setting - storeMode = determineCacheStoreMode( this.properties ); + storeMode = fastSessionServices.getCacheStoreMode( this.properties ); } return CacheModeHelper.interpretCacheMode( storeMode, retrieveMode ); } - private CacheRetrieveMode determineCacheRetrieveMode(Map settings) { + private static CacheRetrieveMode determineCacheRetrieveMode(Map settings) { return ( CacheRetrieveMode ) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE ); } - private CacheStoreMode determineCacheStoreMode(Map settings) { + private static CacheStoreMode determineCacheStoreMode(Map settings) { return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); } @@ -3662,12 +3625,48 @@ public final class SessionImpl return; } + if ( propertyName == null ) { + log.warnf( "Property having key null is illegal; value won't be set." ); + return; + } + + //Store property for future reference: + + if ( properties == null ) { + properties = computeCurrentSessionProperties(); + } properties.put( propertyName, value ); - applyProperties(); + + //now actually update settings, if it's any of these which have a direct impact on this Session state: + + if ( AvailableSettings.FLUSH_MODE.equals( propertyName ) ) { + setHibernateFlushMode( ConfigurationHelper.getFlushMode( value, FlushMode.AUTO ) ); + } + else if ( JPA_LOCK_SCOPE.equals( propertyName ) || JPA_LOCK_TIMEOUT.equals( propertyName ) ) { + setLockOptions( properties, this.lockOptions ); + } + else if ( JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName ) || JPA_SHARED_CACHE_STORE_MODE.equals( propertyName ) ) { + getSession().setCacheMode( + CacheModeHelper.interpretCacheMode( + determineCacheStoreMode( properties ), + determineCacheRetrieveMode( properties ) + ) + ); + } + } + + private Map computeCurrentSessionProperties() { + final HashMap map = new HashMap<>( fastSessionServices.defaultSessionProperties ); + //The FLUSH_MODE is always set at Session creation time, so it needs special treatment to not eagerly initialize this Map: + map.put( AvailableSettings.FLUSH_MODE, getHibernateFlushMode().name() ); + return map; } @Override public Map getProperties() { + if ( properties == null ) { + properties = computeCurrentSessionProperties(); + } return Collections.unmodifiableMap( properties ); } From 4ef8030f5163ff50ccb0c66309e5d2fffa587380 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 13 Aug 2019 10:15:59 +0100 Subject: [PATCH 55/99] HHH-13565 Move field SessionImpl#discardOnClose to FastSessionServices as well --- .../java/org/hibernate/internal/FastSessionServices.java | 2 ++ .../src/main/java/org/hibernate/internal/SessionImpl.java | 8 +------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index af03aaca85..14f1e56d32 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -112,6 +112,7 @@ final class FastSessionServices { final JdbcServices jdbcServices; final boolean isJtaTransactionAccessible; final CacheMode initialSessionCacheMode; + final boolean discardOnClose; //Private fields: private final Dialect dialect; @@ -163,6 +164,7 @@ final class FastSessionServices { this.defaultCacheStoreMode = determineCacheStoreMode( defaultSessionProperties ); this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties ); this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode ); + this.discardOnClose = sf.getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); } Iterable getClearEventListeners() { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index c7d897b090..004149dac0 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -218,8 +218,6 @@ public final class SessionImpl private transient LoadEvent loadEvent; //cached LoadEvent instance - private transient boolean discardOnClose; - private transient TransactionObserver transactionObserver; public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { @@ -232,8 +230,6 @@ public final class SessionImpl this.autoClose = options.shouldAutoClose(); this.queryParametersValidationEnabled = options.isQueryParametersValidationEnabled(); - this.discardOnClose = factory.getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); - if ( options instanceof SharedSessionCreationOptions ) { final SharedSessionCreationOptions sharedOptions = (SharedSessionCreationOptions) options; final ActionQueue.TransactionCompletionProcesses transactionCompletionProcesses = sharedOptions.getTransactionCompletionProcesses(); @@ -345,7 +341,7 @@ public final class SessionImpl // Original hibernate-entitymanager EM#close behavior checkSessionFactoryOpen(); checkOpenOrWaitingForAutoClose(); - if ( discardOnClose || !isTransactionInProgress( false ) ) { + if ( fastSessionServices.discardOnClose || !isTransactionInProgress( false ) ) { super.close(); } else { @@ -3889,7 +3885,5 @@ public final class SessionImpl for ( String filterName : loadQueryInfluencers.getEnabledFilterNames() ) { ( (FilterImpl) loadQueryInfluencers.getEnabledFilter( filterName ) ).afterDeserialize( getFactory() ); } - - this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); } } From 4b2f056a632c64b4bde2ad7e5fe05ace4d50e17c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 13 Aug 2019 20:21:03 +0100 Subject: [PATCH 56/99] HHH-13565 Avoid allocating ArrayList of ConnectionObserver instances on each Session --- .../internal/AbstractSharedSessionContract.java | 7 ++----- .../org/hibernate/internal/FastSessionServices.java | 9 +++++++++ .../org/hibernate/internal/JdbcObserverImpl.java | 12 +++++------- .../hibernate/internal/JdbcSessionContextImpl.java | 7 +++++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index cd5932de2a..5a90b841fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -34,16 +34,13 @@ import org.hibernate.LockMode; import org.hibernate.MultiTenancyStrategy; import org.hibernate.SessionException; import org.hibernate.Transaction; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.cache.spi.CacheTransactionSynchronization; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreator; -import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; -import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -173,7 +170,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont this.jdbcTimeZone = options.getJdbcTimeZone(); final StatementInspector statementInspector = interpret( options.getStatementInspector() ); - this.jdbcSessionContext = new JdbcSessionContextImpl( this, statementInspector ); + this.jdbcSessionContext = new JdbcSessionContextImpl( this, statementInspector, fastSessionServices ); this.entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor ); @@ -1201,7 +1198,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont factory = SessionFactoryImpl.deserialize( ois ); fastSessionServices = factory.getFastSessionServices(); - jdbcSessionContext = new JdbcSessionContextImpl( this, (StatementInspector) ois.readObject() ); + jdbcSessionContext = new JdbcSessionContextImpl( this, (StatementInspector) ois.readObject(), fastSessionServices ); jdbcCoordinator = JdbcCoordinatorImpl.deserialize( ois, this ); cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 14f1e56d32..c0e7a29d48 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -14,6 +14,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEventListener; @@ -41,6 +42,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -118,6 +120,7 @@ final class FastSessionServices { private final Dialect dialect; private final CacheStoreMode defaultCacheStoreMode; private final CacheRetrieveMode defaultCacheRetrieveMode; + private List defaultJdbcObservers; FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); @@ -165,6 +168,7 @@ final class FastSessionServices { this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties ); this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode ); this.discardOnClose = sf.getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); + this.defaultJdbcObservers = Collections.singletonList( new ConnectionObserverStatsBridge( sf ) ); } Iterable getClearEventListeners() { @@ -326,4 +330,9 @@ final class FastSessionServices { private static CacheStoreMode determineCacheStoreMode(Map settings) { return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); } + + public Iterable getDefaultJdbcObservers() { + return defaultJdbcObservers; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java index ef02f759a4..be3cf12978 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java @@ -7,8 +7,6 @@ package org.hibernate.internal; import java.sql.Connection; -import java.util.ArrayList; -import java.util.List; import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -18,13 +16,13 @@ import org.hibernate.resource.jdbc.spi.JdbcObserver; * @author Steve Ebersole */ public class JdbcObserverImpl implements JdbcObserver { - private final SharedSessionContractImplementor session; - private final transient List observers; - public JdbcObserverImpl(SharedSessionContractImplementor session) { + private final SharedSessionContractImplementor session; + private final Iterable observers; + + public JdbcObserverImpl(SharedSessionContractImplementor session, FastSessionServices fastSessionServices) { this.session = session; - this.observers = new ArrayList<>(); - this.observers.add( new ConnectionObserverStatsBridge( session.getFactory() ) ); + this.observers = fastSessionServices.getDefaultJdbcObservers(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/JdbcSessionContextImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/JdbcSessionContextImpl.java index da6c9f3ca7..954593d6d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/JdbcSessionContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/JdbcSessionContextImpl.java @@ -28,12 +28,15 @@ public class JdbcSessionContextImpl implements JdbcSessionContext { private final transient ServiceRegistry serviceRegistry; private final transient JdbcObserver jdbcObserver; - public JdbcSessionContextImpl(SharedSessionContractImplementor session, StatementInspector statementInspector) { + public JdbcSessionContextImpl( + SharedSessionContractImplementor session, + StatementInspector statementInspector, + FastSessionServices fastSessionServices) { this.sessionFactory = session.getFactory(); this.statementInspector = statementInspector; this.connectionHandlingMode = settings().getPhysicalConnectionHandlingMode(); this.serviceRegistry = sessionFactory.getServiceRegistry(); - this.jdbcObserver = new JdbcObserverImpl( session ); + this.jdbcObserver = new JdbcObserverImpl( session, fastSessionServices ); if ( this.statementInspector == null ) { throw new IllegalArgumentException( "StatementInspector cannot be null" ); From 269d5f835860b188df81622d369bc37e61a35ea5 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 22 Aug 2019 08:47:50 +0100 Subject: [PATCH 57/99] HHH-13565 Review allocations for default SessionEventListener instances --- .../BaselineSessionEventsListenerBuilder.java | 71 ++++++++--- .../SessionEventListenerManagerImpl.java | 118 ++++++++++-------- .../AbstractSharedSessionContract.java | 12 +- .../internal/FastSessionServices.java | 9 +- .../NonContextualJdbcConnectionAccess.java | 3 + .../internal/SessionCreationOptions.java | 8 ++ .../internal/SessionFactoryImpl.java | 39 ++++-- .../SessionEventListenersManagerTest.java | 66 ++++++++++ 8 files changed, 243 insertions(+), 83 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/event/SessionEventListenersManagerTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BaselineSessionEventsListenerBuilder.java b/hibernate-core/src/main/java/org/hibernate/cfg/BaselineSessionEventsListenerBuilder.java index 5a2b8a4190..487b201c25 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BaselineSessionEventsListenerBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BaselineSessionEventsListenerBuilder.java @@ -7,6 +7,7 @@ package org.hibernate.cfg; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.hibernate.HibernateException; @@ -17,6 +18,9 @@ import org.hibernate.engine.internal.StatisticalLoggingSessionEventListener; * @author Steve Ebersole */ public class BaselineSessionEventsListenerBuilder { + + private static final SessionEventListener[] EMPTY = new SessionEventListener[0]; + private boolean logSessionMetrics; private Class autoListener; @@ -33,6 +37,10 @@ public class BaselineSessionEventsListenerBuilder { } @SuppressWarnings("UnusedDeclaration") + /** + * @deprecated this method will be removed as this builder should become immutable + */ + @Deprecated public void setLogSessionMetrics(boolean logSessionMetrics) { this.logSessionMetrics = logSessionMetrics; } @@ -43,26 +51,59 @@ public class BaselineSessionEventsListenerBuilder { } @SuppressWarnings("UnusedDeclaration") + /** + * @deprecated this method will be removed as this builder should become immutable + */ + @Deprecated public void setAutoListener(Class autoListener) { this.autoListener = autoListener; } public List buildBaselineList() { - List list = new ArrayList(); - if ( logSessionMetrics && StatisticalLoggingSessionEventListener.isLoggingEnabled() ) { - list.add( new StatisticalLoggingSessionEventListener() ); - } - if ( autoListener != null ) { - try { - list.add( autoListener.newInstance() ); - } - catch (Exception e) { - throw new HibernateException( - "Unable to instantiate specified auto SessionEventListener : " + autoListener.getName(), - e - ); - } - } + final SessionEventListener[] sessionEventListeners = buildBaseline(); + //Capacity: needs to hold at least all elements from the baseline, but also expect to add a little more later. + ArrayList list = new ArrayList<>( sessionEventListeners.length + 3 ); + Collections.addAll( list, sessionEventListeners ); return list; } + + public SessionEventListener[] buildBaseline() { + final boolean addStats = logSessionMetrics && StatisticalLoggingSessionEventListener.isLoggingEnabled(); + final boolean addAutoListener = autoListener != null; + final SessionEventListener[] arr; + if ( addStats && addAutoListener ) { + arr = new SessionEventListener[2]; + arr[0] = buildStatsListener(); + arr[1] = buildAutoListener( autoListener ); + } + else if ( !addStats && !addAutoListener ) { + arr = EMPTY; + } + else if ( !addStats && addAutoListener ) { + arr = new SessionEventListener[1]; + arr[0] = buildAutoListener( autoListener ); + } + else { //Last case: if ( addStats && !addAutoListener ) + arr = new SessionEventListener[1]; + arr[0] = buildStatsListener(); + } + return arr; + } + + private static SessionEventListener buildAutoListener(final Class autoListener) { + try { + return autoListener.newInstance(); + } + catch (Exception e) { + throw new HibernateException( + "Unable to instantiate specified auto SessionEventListener : " + autoListener.getName(), + e + ); + } + } + + private static SessionEventListener buildStatsListener() { + return new StatisticalLoggingSessionEventListener(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java index a79dac9657..52261bfeab 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java @@ -7,8 +7,8 @@ package org.hibernate.engine.internal; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.Objects; import org.hibernate.SessionEventListener; import org.hibernate.engine.spi.SessionEventListenerManager; @@ -17,255 +17,269 @@ import org.hibernate.engine.spi.SessionEventListenerManager; * @author Steve Ebersole */ public class SessionEventListenerManagerImpl implements SessionEventListenerManager, Serializable { - private List listenerList; + + private SessionEventListener[] listeners; + + public SessionEventListenerManagerImpl(SessionEventListener... initialListener) { + //no need for defensive copies until the array is mutated: + this.listeners = initialListener; + } @Override - public void addListener(SessionEventListener... listeners) { - if ( listenerList == null ) { - listenerList = new ArrayList<>(); + public void addListener(final SessionEventListener... additionalListeners) { + Objects.requireNonNull( additionalListeners ); + final SessionEventListener[] existing = this.listeners; + if ( existing == null ) { + //Make a defensive copy as this array can be tracked back to API (user code) + this.listeners = Arrays.copyOf( additionalListeners, additionalListeners.length ); + } + else { + // Resize our existing array and add the new listeners + final SessionEventListener[] newlist = new SessionEventListener[ existing.length + additionalListeners.length ]; + System.arraycopy( existing, 0, newlist, 0, existing.length ); + System.arraycopy( additionalListeners, 0, newlist, existing.length, additionalListeners.length ); + this.listeners = newlist; } - - java.util.Collections.addAll( listenerList, listeners ); } @Override public void transactionCompletion(boolean successful) { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.transactionCompletion( successful ); } } @Override public void jdbcConnectionAcquisitionStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcConnectionAcquisitionStart(); } } @Override public void jdbcConnectionAcquisitionEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcConnectionAcquisitionEnd(); } } @Override public void jdbcConnectionReleaseStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcConnectionReleaseStart(); } } @Override public void jdbcConnectionReleaseEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcConnectionReleaseEnd(); } } @Override public void jdbcPrepareStatementStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcPrepareStatementStart(); } } @Override public void jdbcPrepareStatementEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcPrepareStatementEnd(); } } @Override public void jdbcExecuteStatementStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcExecuteStatementStart(); } } @Override public void jdbcExecuteStatementEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcExecuteStatementEnd(); } } @Override public void jdbcExecuteBatchStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcExecuteBatchStart(); } } @Override public void jdbcExecuteBatchEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.jdbcExecuteBatchEnd(); } } @Override public void cachePutStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.cachePutStart(); } } @Override public void cachePutEnd() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.cachePutEnd(); } } @Override public void cacheGetStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.cacheGetStart(); } } @Override public void cacheGetEnd(boolean hit) { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.cacheGetEnd( hit ); } } @Override public void flushStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.flushStart(); } } @Override public void flushEnd(int numberOfEntities, int numberOfCollections) { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.flushEnd( numberOfEntities, numberOfCollections ); } } @Override public void partialFlushStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.partialFlushStart(); } } @Override public void partialFlushEnd(int numberOfEntities, int numberOfCollections) { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.partialFlushEnd( numberOfEntities, numberOfCollections ); } } @Override public void dirtyCalculationStart() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.dirtyCalculationStart(); } } @Override public void dirtyCalculationEnd(boolean dirty) { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.dirtyCalculationEnd( dirty ); } } @Override public void end() { - if ( listenerList == null ) { + if ( listeners == null ) { return; } - for ( SessionEventListener listener : listenerList ) { + for ( SessionEventListener listener : listeners ) { listener.end(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 5a90b841fd..d89aeb710b 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -32,6 +32,7 @@ import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.LockMode; import org.hibernate.MultiTenancyStrategy; +import org.hibernate.SessionEventListener; import org.hibernate.SessionException; import org.hibernate.Transaction; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; @@ -136,7 +137,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont protected boolean waitingForAutoClose; // transient & non-final for Serialization purposes - ugh - private transient SessionEventListenerManagerImpl sessionEventsManager = new SessionEventListenerManagerImpl(); + private transient SessionEventListenerManagerImpl sessionEventsManager; private transient EntityNameResolver entityNameResolver; private Integer jdbcBatchSize; @@ -168,6 +169,13 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont this.interceptor = interpret( options.getInterceptor() ); this.jdbcTimeZone = options.getJdbcTimeZone(); + final List customSessionEventListener = options.getCustomSessionEventListener(); + if ( customSessionEventListener == null ) { + sessionEventsManager = new SessionEventListenerManagerImpl( fastSessionServices.defaultSessionEventListeners.buildBaseline() ); + } + else { + sessionEventsManager = new SessionEventListenerManagerImpl( customSessionEventListener.toArray( new SessionEventListener[0] ) ); + } final StatementInspector statementInspector = interpret( options.getStatementInspector() ); this.jdbcSessionContext = new JdbcSessionContextImpl( this, statementInspector, fastSessionServices ); @@ -1190,7 +1198,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Step 1 :: read back non-transient state... ois.defaultReadObject(); - sessionEventsManager = new SessionEventListenerManagerImpl(); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Step 2 :: read back transient state... @@ -1198,6 +1205,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont factory = SessionFactoryImpl.deserialize( ois ); fastSessionServices = factory.getFastSessionServices(); + sessionEventsManager = new SessionEventListenerManagerImpl( fastSessionServices.defaultSessionEventListeners.buildBaseline() ); jdbcSessionContext = new JdbcSessionContextImpl( this, (StatementInspector) ois.readObject(), fastSessionServices ); jdbcCoordinator = JdbcCoordinatorImpl.deserialize( ois, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index c0e7a29d48..afa3bbede1 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -10,6 +10,8 @@ import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockOptions; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.cfg.BaselineSessionEventsListenerBuilder; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -115,6 +117,7 @@ final class FastSessionServices { final boolean isJtaTransactionAccessible; final CacheMode initialSessionCacheMode; final boolean discardOnClose; + final BaselineSessionEventsListenerBuilder defaultSessionEventListeners; //Private fields: private final Dialect dialect; @@ -126,6 +129,7 @@ final class FastSessionServices { Objects.requireNonNull( sf ); final ServiceRegistryImplementor sr = sf.getServiceRegistry(); final JdbcServices jdbcServices = sf.getJdbcServices(); + final SessionFactoryOptions sessionFactoryOptions = sf.getSessionFactoryOptions(); // Pre-compute all iterators on Event listeners: final EventListenerRegistry eventListenerRegistry = sr.getService( EventListenerRegistry.class ); @@ -150,7 +154,7 @@ final class FastSessionServices { //Other highly useful constants: this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); - this.disallowOutOfTransactionUpdateOperations = !sf.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations(); + this.disallowOutOfTransactionUpdateOperations = !sessionFactoryOptions.isAllowOutOfTransactionUpdateOperations(); this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob(); this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); @@ -167,8 +171,9 @@ final class FastSessionServices { this.defaultCacheStoreMode = determineCacheStoreMode( defaultSessionProperties ); this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties ); this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode ); - this.discardOnClose = sf.getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); + this.discardOnClose = sessionFactoryOptions.isReleaseResourcesOnCloseEnabled(); this.defaultJdbcObservers = Collections.singletonList( new ConnectionObserverStatsBridge( sf ) ); + this.defaultSessionEventListeners = sessionFactoryOptions.getBaselineSessionEventsListenerBuilder(); } Iterable getClearEventListeners() { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java index 4cbe9573c9..82df15f40f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java @@ -9,6 +9,7 @@ package org.hibernate.internal; import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; import org.hibernate.SessionEventListener; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -24,6 +25,8 @@ public class NonContextualJdbcConnectionAccess implements JdbcConnectionAccess, public NonContextualJdbcConnectionAccess( SessionEventListener listener, ConnectionProvider connectionProvider) { + Objects.requireNonNull( listener ); + Objects.requireNonNull( connectionProvider ); this.listener = listener; this.connectionProvider = connectionProvider; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java index a647708e18..ed2920662e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java @@ -7,10 +7,12 @@ package org.hibernate.internal; import java.sql.Connection; +import java.util.List; import java.util.TimeZone; import org.hibernate.FlushMode; import org.hibernate.Interceptor; +import org.hibernate.SessionEventListener; import org.hibernate.engine.spi.SessionOwner; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -45,6 +47,12 @@ public interface SessionCreationOptions { TimeZone getJdbcTimeZone(); + /** + * @return the full list of SessionEventListener if this was customized, + * or null if this Session is being created with the default list. + */ + List getCustomSessionEventListener(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // deprecations diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index af25ea4876..573863847f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -1156,6 +1156,9 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { private TimeZone jdbcTimeZone; private boolean queryParametersValidationEnabled; + // Lazy: defaults can be built by invoking the builder in fastSessionServices.defaultSessionEventListeners + // (Need a fresh build for each Session as the listener instances can't be reused across sessions) + // Only initialize of the builder is overriding the default. private List listeners; //todo : expose setting @@ -1178,9 +1181,7 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { tenantIdentifier = currentTenantIdentifierResolver.resolveCurrentTenantIdentifier(); } this.jdbcTimeZone = sessionFactoryOptions.getJdbcTimeZone(); - - listeners = sessionFactoryOptions.getBaselineSessionEventsListenerBuilder().buildBaselineList(); - queryParametersValidationEnabled = sessionFactoryOptions.isQueryParametersValidationEnabled(); + this.queryParametersValidationEnabled = sessionFactoryOptions.isQueryParametersValidationEnabled(); } @@ -1268,20 +1269,18 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { return jdbcTimeZone; } + @Override + public List getCustomSessionEventListener() { + return listeners; + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SessionBuilder @Override public Session openSession() { log.tracef( "Opening Hibernate Session. tenant=%s", tenantIdentifier ); - final SessionImpl session = new SessionImpl( sessionFactory, this ); - - final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); - for ( SessionEventListener listener : listeners ) { - eventListenerManager.addListener( listener ); - } - - return session; + return new SessionImpl( sessionFactory, this ); } @Override @@ -1377,6 +1376,11 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { @Override @SuppressWarnings("unchecked") public T eventListeners(SessionEventListener... listeners) { + if ( this.listeners == null ) { + this.listeners = sessionFactory.getSessionFactoryOptions() + .getBaselineSessionEventsListenerBuilder() + .buildBaselineList(); + } Collections.addAll( this.listeners, listeners ); return (T) this; } @@ -1384,7 +1388,13 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { @Override @SuppressWarnings("unchecked") public T clearEventListeners() { - listeners.clear(); + if ( listeners == null ) { + //Needs to initialize explicitly to an empty list as otherwise "null" immplies the default listeners will be applied + this.listeners = new ArrayList( 3 ); + } + else { + listeners.clear(); + } return (T) this; } @@ -1484,6 +1494,11 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone(); } + @Override + public List getCustomSessionEventListener() { + return null; + } + @Override public SessionOwner getSessionOwner() { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/event/SessionEventListenersManagerTest.java b/hibernate-core/src/test/java/org/hibernate/event/SessionEventListenersManagerTest.java new file mode 100644 index 0000000000..d4eae4acda --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/event/SessionEventListenersManagerTest.java @@ -0,0 +1,66 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event; + +import org.hibernate.BaseSessionEventListener; +import org.hibernate.SessionEventListener; +import org.hibernate.engine.internal.SessionEventListenerManagerImpl; + +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Assert; +import org.junit.Test; + +public class SessionEventListenersManagerTest extends BaseUnitTestCase { + + @Test + public void testListenerAppending() { + StringBuilder sb = new StringBuilder(); + SessionEventListener a = new TestSessionEventListener( sb , 'a' ); + SessionEventListener b = new TestSessionEventListener( sb , 'b' ); + SessionEventListener c = new TestSessionEventListener( sb , 'c' ); + SessionEventListener d = new TestSessionEventListener( sb , 'd' ); + SessionEventListenerManagerImpl l = new SessionEventListenerManagerImpl( a, b ); + l.addListener( c, d ); + l.dirtyCalculationEnd( true ); + Assert.assertEquals( "abcd", sb.toString() ); + l.dirtyCalculationEnd( true ); + Assert.assertEquals( "abcdabcd", sb.toString() ); + l.addListener( new TestSessionEventListener( sb , 'e' ) ); + l.dirtyCalculationEnd( true ); + Assert.assertEquals( "abcdabcdabcde", sb.toString() ); + } + + @Test + public void testEmptyListenerAppending() { + StringBuilder sb = new StringBuilder(); + SessionEventListenerManagerImpl l = new SessionEventListenerManagerImpl(); + l.dirtyCalculationEnd( true ); + Assert.assertEquals( "", sb.toString() ); + l.addListener( new TestSessionEventListener( sb , 'e' ) ); + l.dirtyCalculationEnd( true ); + Assert.assertEquals( "e", sb.toString() ); + } + + private static class TestSessionEventListener extends BaseSessionEventListener { + + private final StringBuilder sb; + private final char theChar; + + public TestSessionEventListener(StringBuilder sb, char theChar) { + this.sb = sb; + this.theChar = theChar; + } + + //Just picking any method. This one is funny.. + @Override + public void dirtyCalculationEnd(boolean dirty) { + sb.append( theChar ); + } + + } + +} From b8f3fc36164a5bb70349039ba6325d560a77a041 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 19 Aug 2019 22:31:24 +0100 Subject: [PATCH 58/99] HHH-13565 Convert iteration of event listeners to be allocation free --- .../internal/EventListenerGroupImpl.java | 27 +++- .../event/service/spi/EventListenerGroup.java | 24 +++ .../internal/FastSessionServices.java | 150 +++++------------- .../org/hibernate/internal/SessionImpl.java | 136 +++++----------- 4 files changed, 132 insertions(+), 205 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java index 8d5200c751..20e9d5c40a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java @@ -9,9 +9,10 @@ package org.hibernate.event.service.internal; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.ListIterator; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Supplier; import org.hibernate.event.service.spi.DuplicationStrategy; import org.hibernate.event.service.spi.EventListenerGroup; @@ -28,7 +29,10 @@ class EventListenerGroupImpl implements EventListenerGroup { private final EventListenerRegistryImpl listenerRegistry; private final Set duplicationStrategies = new LinkedHashSet<>(); - private List listeners; + + // Performance: make sure a forEach iteration on this type is efficient; in particular we choose ArrayList + // so to avoid allocating iterators. + private ArrayList listeners; public EventListenerGroupImpl(EventType eventType, EventListenerRegistryImpl listenerRegistry) { this.eventType = eventType; @@ -75,6 +79,25 @@ class EventListenerGroupImpl implements EventListenerGroup { } } + @Override + public final void fireLazyEventOnEachListener(final Supplier eventSupplier, final BiConsumer actionOnEvent) { + if ( listeners != null && listeners.size() != 0 ) { + final U event = eventSupplier.get(); + for ( T listener : this.listeners ) { + actionOnEvent.accept( listener, event ); + } + } + } + + @Override + public final void fireEventOnEachListener(final U event, final BiConsumer actionOnEvent) { + if ( listeners != null ) { + for ( T listener : this.listeners ) { + actionOnEvent.accept( listener, event ); + } + } + } + @Override public void addDuplicationStrategy(DuplicationStrategy strategy) { duplicationStrategies.add( strategy ); diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java index fad69d143d..1f4e823a84 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java @@ -7,6 +7,8 @@ package org.hibernate.event.service.spi; import java.io.Serializable; +import java.util.function.BiConsumer; +import java.util.function.Supplier; import org.hibernate.event.spi.EventType; @@ -54,4 +56,26 @@ public interface EventListenerGroup extends Serializable { public void clear(); + /** + * Fires an event on each registered event listener of this group. + * + * Implementation note (performance): + * the first argument is a supplier so that events can avoid allocation when no listener is registered. + * the second argument is specifically designed to avoid needing a capturing lambda. + * + * @param eventSupplier + * @param actionOnEvent + * @param the kind of event + */ + void fireLazyEventOnEachListener(final Supplier eventSupplier, final BiConsumer actionOnEvent); + + /** + * Similar as {@link #fireLazyEventOnEachListener(Supplier, BiConsumer)} except it doesn't use a {{@link Supplier}}: + * useful when there is no need to lazily initialize the event. + * @param event + * @param actionOnEvent + * @param the kind of event + */ + void fireEventOnEachListener(final U event, final BiConsumer actionOnEvent); + } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index afa3bbede1..f3367e2701 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -18,6 +18,7 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.AutoFlushEventListener; import org.hibernate.event.spi.ClearEventListener; @@ -85,25 +86,24 @@ final class FastSessionServices { final Map defaultSessionProperties; // All session events need to be iterated frequently: - - private final Iterable clearEventListeners; - private final Iterable saveUpdateEventListeners; - private final Iterable evictEventListeners; - private final Iterable saveEventListeners; - private final Iterable updateEventListeners; - private final Iterable lockEventListeners; - private final Iterable persistEventListeners; - private final Iterable persistOnFlushEventListeners; - private final Iterable mergeEventListeners; - private final Iterable deleteEventListeners; - private final Iterable loadEventListeners; - private final Iterable refreshEventListeners; - private final Iterable replicateEventListeners; - private final Iterable autoFlushEventListeners; - private final Iterable dirtyCheckEventListeners; - private final Iterable flushEventListeners; - private final Iterable initCollectionEventListeners; - private final Iterable resolveNaturalIdEventListeners; + final EventListenerGroup eventListenerGroup_AUTO_FLUSH; + final EventListenerGroup eventListenerGroup_CLEAR; + final EventListenerGroup eventListenerGroup_DELETE; + final EventListenerGroup eventListenerGroup_DIRTY_CHECK; + final EventListenerGroup eventListenerGroup_EVICT; + final EventListenerGroup eventListenerGroup_FLUSH; + final EventListenerGroup eventListenerGroup_INIT_COLLECTION; + final EventListenerGroup eventListenerGroup_LOAD; + final EventListenerGroup eventListenerGroup_LOCK; + final EventListenerGroup eventListenerGroup_MERGE; + final EventListenerGroup eventListenerGroup_PERSIST; + final EventListenerGroup eventListenerGroup_PERSIST_ONFLUSH; + final EventListenerGroup eventListenerGroup_REFRESH; + final EventListenerGroup eventListenerGroup_REPLICATE; + final EventListenerGroup eventListenerGroup_RESOLVE_NATURAL_ID; + final EventListenerGroup eventListenerGroup_SAVE; + final EventListenerGroup eventListenerGroup_SAVE_UPDATE; + final EventListenerGroup eventListenerGroup_UPDATE; //Intentionally Package private: final boolean disallowOutOfTransactionUpdateOperations; @@ -133,24 +133,24 @@ final class FastSessionServices { // Pre-compute all iterators on Event listeners: final EventListenerRegistry eventListenerRegistry = sr.getService( EventListenerRegistry.class ); - this.clearEventListeners = listeners( eventListenerRegistry, EventType.CLEAR ); - this.saveUpdateEventListeners = listeners( eventListenerRegistry, EventType.SAVE_UPDATE ); - this.evictEventListeners = listeners( eventListenerRegistry, EventType.EVICT ); - this.saveEventListeners = listeners( eventListenerRegistry, EventType.SAVE ); - this.updateEventListeners = listeners( eventListenerRegistry, EventType.UPDATE ); - this.lockEventListeners = listeners( eventListenerRegistry, EventType.LOCK ); - this.persistEventListeners = listeners( eventListenerRegistry, EventType.PERSIST ); - this.persistOnFlushEventListeners = listeners( eventListenerRegistry, EventType.PERSIST_ONFLUSH ); - this.mergeEventListeners = listeners( eventListenerRegistry, EventType.MERGE ); - this.deleteEventListeners = listeners( eventListenerRegistry, EventType.DELETE ); - this.loadEventListeners = listeners( eventListenerRegistry, EventType.LOAD ); - this.refreshEventListeners = listeners( eventListenerRegistry, EventType.REFRESH ); - this.replicateEventListeners = listeners( eventListenerRegistry, EventType.REPLICATE ); - this.autoFlushEventListeners = listeners( eventListenerRegistry, EventType.AUTO_FLUSH ); - this.dirtyCheckEventListeners = listeners( eventListenerRegistry, EventType.DIRTY_CHECK ); - this.flushEventListeners = listeners( eventListenerRegistry, EventType.FLUSH ); - this.initCollectionEventListeners = listeners( eventListenerRegistry, EventType.INIT_COLLECTION ); - this.resolveNaturalIdEventListeners = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID ); + this.eventListenerGroup_AUTO_FLUSH = listeners( eventListenerRegistry, EventType.AUTO_FLUSH ); + this.eventListenerGroup_CLEAR = listeners( eventListenerRegistry, EventType.CLEAR ); + this.eventListenerGroup_DELETE = listeners( eventListenerRegistry, EventType.DELETE ); + this.eventListenerGroup_DIRTY_CHECK = listeners( eventListenerRegistry, EventType.DIRTY_CHECK ); + this.eventListenerGroup_EVICT = listeners( eventListenerRegistry, EventType.EVICT ); + this.eventListenerGroup_FLUSH = listeners( eventListenerRegistry, EventType.FLUSH ); + this.eventListenerGroup_INIT_COLLECTION = listeners( eventListenerRegistry, EventType.INIT_COLLECTION ); + this.eventListenerGroup_LOAD = listeners( eventListenerRegistry, EventType.LOAD ); + this.eventListenerGroup_LOCK = listeners( eventListenerRegistry, EventType.LOCK ); + this.eventListenerGroup_MERGE = listeners( eventListenerRegistry, EventType.MERGE ); + this.eventListenerGroup_PERSIST = listeners( eventListenerRegistry, EventType.PERSIST ); + this.eventListenerGroup_PERSIST_ONFLUSH = listeners( eventListenerRegistry, EventType.PERSIST_ONFLUSH ); + this.eventListenerGroup_REFRESH = listeners( eventListenerRegistry, EventType.REFRESH ); + this.eventListenerGroup_REPLICATE = listeners( eventListenerRegistry, EventType.REPLICATE ); + this.eventListenerGroup_RESOLVE_NATURAL_ID = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID ); + this.eventListenerGroup_SAVE = listeners( eventListenerRegistry, EventType.SAVE ); + this.eventListenerGroup_SAVE_UPDATE = listeners( eventListenerRegistry, EventType.SAVE_UPDATE ); + this.eventListenerGroup_UPDATE = listeners( eventListenerRegistry, EventType.UPDATE ); //Other highly useful constants: this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); @@ -176,80 +176,8 @@ final class FastSessionServices { this.defaultSessionEventListeners = sessionFactoryOptions.getBaselineSessionEventsListenerBuilder(); } - Iterable getClearEventListeners() { - return clearEventListeners; - } - - Iterable getEvictEventListeners() { - return this.evictEventListeners; - } - - Iterable getDirtyCheckEventListeners() { - return this.dirtyCheckEventListeners; - } - - Iterable getSaveUpdateEventListeners() { - return this.saveUpdateEventListeners; - } - - Iterable getSaveEventListeners() { - return this.saveEventListeners; - } - - Iterable getUpdateEventListeners() { - return this.updateEventListeners; - } - - Iterable getLockEventListeners() { - return this.lockEventListeners; - } - - Iterable getPersistEventListeners() { - return persistEventListeners; - } - - Iterable getFlushEventListeners() { - return flushEventListeners; - } - - Iterable getPersistOnFlushEventListeners() { - return persistOnFlushEventListeners; - } - - Iterable getInitCollectionEventListeners() { - return initCollectionEventListeners; - } - - Iterable getLoadEventListeners() { - return loadEventListeners; - } - - Iterable getMergeEventListeners() { - return mergeEventListeners; - } - - Iterable getRefreshEventListeners() { - return refreshEventListeners; - } - - Iterable getDeleteEventListeners() { - return deleteEventListeners; - } - - Iterable getAutoFlushEventListeners() { - return autoFlushEventListeners; - } - - Iterable getReplicateEventListeners() { - return replicateEventListeners; - } - - Iterable getResolveNaturalIdEventListeners() { - return resolveNaturalIdEventListeners; - } - - private static Iterable listeners(EventListenerRegistry elr, EventType type) { - return elr.getEventListenerGroup( type ).listeners(); + private static EventListenerGroup listeners(EventListenerRegistry elr, EventType type) { + return elr.getEventListenerGroup( type ); } SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 004149dac0..7b1a04c847 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -307,13 +307,12 @@ public final class SessionImpl persistenceContext.clear(); actionQueue.clear(); - final ClearEvent event = new ClearEvent( this ); - for ( ClearEventListener listener : fastSessionServices.getClearEventListeners() ) { - listener.onClear( event ); - } + fastSessionServices.eventListenerGroup_CLEAR.fireLazyEventOnEachListener( this::createClearEvent, ClearEventListener::onClear ); } - + private ClearEvent createClearEvent() { + return new ClearEvent( this ); + } @Override @SuppressWarnings("StatementWithEmptyBody") @@ -578,13 +577,11 @@ public final class SessionImpl fireSaveOrUpdate( new SaveOrUpdateEvent( entityName, obj, this ) ); } - private void fireSaveOrUpdate(SaveOrUpdateEvent event) { + private void fireSaveOrUpdate(final SaveOrUpdateEvent event) { checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : fastSessionServices.getSaveUpdateEventListeners() ) { - listener.onSaveOrUpdate( event ); - } + fastSessionServices.eventListenerGroup_SAVE_UPDATE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate ); checkNoUnresolvedActionsAfterOperation(); } @@ -600,13 +597,11 @@ public final class SessionImpl return fireSave( new SaveOrUpdateEvent( entityName, object, this ) ); } - private Serializable fireSave(SaveOrUpdateEvent event) { + private Serializable fireSave(final SaveOrUpdateEvent event) { checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : fastSessionServices.getSaveEventListeners() ) { - listener.onSaveOrUpdate( event ); - } + fastSessionServices.eventListenerGroup_SAVE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate ); checkNoUnresolvedActionsAfterOperation(); return event.getResultId(); } @@ -628,9 +623,7 @@ public final class SessionImpl checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( SaveOrUpdateEventListener listener : fastSessionServices.getUpdateEventListeners() ) { - listener.onSaveOrUpdate( event ); - } + fastSessionServices.eventListenerGroup_UPDATE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate ); checkNoUnresolvedActionsAfterOperation(); } @@ -663,13 +656,10 @@ public final class SessionImpl private void fireLock(LockEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( LockEventListener listener : fastSessionServices.getLockEventListeners() ) { - listener.onLock( event ); - } + fastSessionServices.eventListenerGroup_LOCK.fireEventOnEachListener( event, LockEventListener::onLock ); delayedAfterCompletion(); } - // persist() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override @@ -690,14 +680,12 @@ public final class SessionImpl firePersist( copiedAlready, new PersistEvent( entityName, object, this ) ); } - private void firePersist(PersistEvent event) { + private void firePersist(final PersistEvent event) { try { checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( PersistEventListener listener : fastSessionServices.getPersistEventListeners() ) { - listener.onPersist( event ); - } + fastSessionServices.eventListenerGroup_PERSIST.fireEventOnEachListener( event, PersistEventListener::onPersist ); } catch (MappingException e) { throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage() ) ); @@ -715,13 +703,13 @@ public final class SessionImpl } } - private void firePersist(Map copiedAlready, PersistEvent event) { + private void firePersist(final Map copiedAlready, final PersistEvent event) { pulseTransactionCoordinator(); try { - for ( PersistEventListener listener : fastSessionServices.getPersistEventListeners() ) { - listener.onPersist( event, copiedAlready ); - } + //Uses a capturing lambda in this case as we need to carry the additional Map parameter: + fastSessionServices.eventListenerGroup_PERSIST + .fireEventOnEachListener( event, (l, e) -> l.onPersist( e, copiedAlready ) ); } catch ( MappingException e ) { throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage() ) ) ; @@ -752,22 +740,18 @@ public final class SessionImpl firePersistOnFlush( copiedAlready, new PersistEvent( entityName, object, this ) ); } - private void firePersistOnFlush(Map copiedAlready, PersistEvent event) { + private void firePersistOnFlush(final Map copiedAlready, final PersistEvent event) { checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); - for ( PersistEventListener listener : fastSessionServices.getPersistOnFlushEventListeners() ) { - listener.onPersist( event, copiedAlready ); - } + fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, (l,e) -> l.onPersist( e, copiedAlready ) ); delayedAfterCompletion(); } - private void firePersistOnFlush(PersistEvent event) { + private void firePersistOnFlush(final PersistEvent event) { checkOpen(); checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( PersistEventListener listener : fastSessionServices.getPersistOnFlushEventListeners() ) { - listener.onPersist( event ); - } + fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, PersistEventListener::onPersist ); checkNoUnresolvedActionsAfterOperation(); } @@ -796,9 +780,7 @@ public final class SessionImpl try { checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - for ( MergeEventListener listener : fastSessionServices.getMergeEventListeners() ) { - listener.onMerge( event ); - } + fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, MergeEventListener::onMerge ); checkNoUnresolvedActionsAfterOperation(); } catch ( ObjectDeletedException sse ) { @@ -815,12 +797,10 @@ public final class SessionImpl return event.getResult(); } - private void fireMerge(Map copiedAlready, MergeEvent event) { + private void fireMerge(final Map copiedAlready, final MergeEvent event) { try { pulseTransactionCoordinator(); - for ( MergeEventListener listener : fastSessionServices.getMergeEventListeners() ) { - listener.onMerge( event, copiedAlready ); - } + fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, (l,e) -> l.onMerge( e, copiedAlready ) ); } catch ( ObjectDeletedException sse ) { throw getExceptionConverter().convert( new IllegalArgumentException( sse ) ); @@ -905,12 +885,10 @@ public final class SessionImpl } } - private void fireDelete(DeleteEvent event) { + private void fireDelete(final DeleteEvent event) { try{ pulseTransactionCoordinator(); - for ( DeleteEventListener listener : fastSessionServices.getDeleteEventListeners() ) { - listener.onDelete( event ); - } + fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, DeleteEventListener::onDelete ); } catch ( ObjectDeletedException sse ) { throw getExceptionConverter().convert( new IllegalArgumentException( sse ) ); @@ -927,12 +905,10 @@ public final class SessionImpl } } - private void fireDelete(DeleteEvent event, Set transientEntities) { + private void fireDelete(final DeleteEvent event, final Set transientEntities) { try{ pulseTransactionCoordinator(); - for ( DeleteEventListener listener : fastSessionServices.getDeleteEventListeners() ) { - listener.onDelete( event, transientEntities ); - } + fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, (l,e) -> l.onDelete( e, transientEntities ) ); } catch ( ObjectDeletedException sse ) { throw getExceptionConverter().convert( new IllegalArgumentException( sse ) ); @@ -1205,19 +1181,15 @@ public final class SessionImpl // so to skip the session open, transaction synch, etc.. checks, // which have been proven to be not particularly cheap: // it seems they prevent these hot methods from being inlined. - private void fireLoadNoChecks(LoadEvent event, LoadType loadType) { + private void fireLoadNoChecks(final LoadEvent event, final LoadType loadType) { pulseTransactionCoordinator(); - for ( LoadEventListener listener : fastSessionServices.getLoadEventListeners() ) { - listener.onLoad( event, loadType ); - } + fastSessionServices.eventListenerGroup_LOAD.fireEventOnEachListener( event, (l,e) -> l.onLoad( e, loadType ) ); } - private void fireResolveNaturalId(ResolveNaturalIdEvent event) { + private void fireResolveNaturalId(final ResolveNaturalIdEvent event) { checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); - for ( ResolveNaturalIdEventListener listener : fastSessionServices.getResolveNaturalIdEventListeners() ) { - listener.onResolveNaturalId( event ); - } + fastSessionServices.eventListenerGroup_RESOLVE_NATURAL_ID.fireEventOnEachListener( event, ResolveNaturalIdEventListener::onResolveNaturalId ); delayedAfterCompletion(); } @@ -1260,7 +1232,7 @@ public final class SessionImpl fireRefresh( refreshedAlready, new RefreshEvent( entityName, object, this ) ); } - private void fireRefresh(RefreshEvent event) { + private void fireRefresh(final RefreshEvent event) { try { if ( !getSessionFactory().getSessionFactoryOptions().isAllowRefreshDetachedEntity() ) { if ( event.getEntityName() != null ) { @@ -1275,9 +1247,7 @@ public final class SessionImpl } } pulseTransactionCoordinator(); - for ( RefreshEventListener listener : fastSessionServices.getRefreshEventListeners() ) { - listener.onRefresh( event ); - } + fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, RefreshEventListener::onRefresh ); } catch (RuntimeException e) { if ( !getSessionFactory().getSessionFactoryOptions().isJpaBootstrap() ) { @@ -1293,12 +1263,10 @@ public final class SessionImpl } } - private void fireRefresh(Map refreshedAlready, RefreshEvent event) { + private void fireRefresh(final Map refreshedAlready, final RefreshEvent event) { try { pulseTransactionCoordinator(); - for ( RefreshEventListener listener : fastSessionServices.getRefreshEventListeners() ) { - listener.onRefresh( event, refreshedAlready ); - } + fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, (l,e) -> l.onRefresh( e, refreshedAlready ) ); } catch (RuntimeException e) { throw getExceptionConverter().convert( e ); @@ -1322,12 +1290,10 @@ public final class SessionImpl fireReplicate( new ReplicateEvent( entityName, obj, replicationMode, this ) ); } - private void fireReplicate(ReplicateEvent event) { + private void fireReplicate(final ReplicateEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( ReplicateEventListener listener : fastSessionServices.getReplicateEventListeners() ) { - listener.onReplicate( event ); - } + fastSessionServices.eventListenerGroup_REPLICATE.fireEventOnEachListener( event, ReplicateEventListener::onReplicate ); delayedAfterCompletion(); } @@ -1340,15 +1306,10 @@ public final class SessionImpl */ @Override public void evict(Object object) throws HibernateException { - fireEvict( new EvictEvent( object, this ) ); - } - - private void fireEvict(EvictEvent event) { checkOpen(); pulseTransactionCoordinator(); - for ( EvictEventListener listener : fastSessionServices.getEvictEventListeners() ) { - listener.onEvict( event ); - } + final EvictEvent event = new EvictEvent( object, this ); + fastSessionServices.eventListenerGroup_EVICT.fireEventOnEachListener( event, EvictEventListener::onEvict ); delayedAfterCompletion(); } @@ -1363,9 +1324,7 @@ public final class SessionImpl return false; } AutoFlushEvent event = new AutoFlushEvent( querySpaces, this ); - for ( AutoFlushEventListener listener : fastSessionServices.getAutoFlushEventListeners() ) { - listener.onAutoFlush( event ); - } + fastSessionServices.eventListenerGroup_AUTO_FLUSH.fireEventOnEachListener( event, AutoFlushEventListener::onAutoFlush ); return event.isFlushRequired(); } @@ -1379,9 +1338,7 @@ public final class SessionImpl return true; } DirtyCheckEvent event = new DirtyCheckEvent( this ); - for ( DirtyCheckEventListener listener : fastSessionServices.getDirtyCheckEventListeners() ) { - listener.onDirtyCheck( event ); - } + fastSessionServices.eventListenerGroup_DIRTY_CHECK.fireEventOnEachListener( event, DirtyCheckEventListener::onDirtyCheck ); delayedAfterCompletion(); return event.isDirty(); } @@ -1401,11 +1358,8 @@ public final class SessionImpl throw new HibernateException( "Flush during cascade is dangerous" ); } - FlushEvent flushEvent = new FlushEvent( this ); - for ( FlushEventListener listener : fastSessionServices.getFlushEventListeners() ) { - listener.onFlush( flushEvent ); - } - + FlushEvent event = new FlushEvent( this ); + fastSessionServices.eventListenerGroup_FLUSH.fireEventOnEachListener( event, FlushEventListener::onFlush ); delayedAfterCompletion(); } catch ( RuntimeException e ) { @@ -2206,9 +2160,7 @@ public final class SessionImpl checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); InitializeCollectionEvent event = new InitializeCollectionEvent( collection, this ); - for ( InitializeCollectionEventListener listener : fastSessionServices.getInitCollectionEventListeners() ) { - listener.onInitializeCollection( event ); - } + fastSessionServices.eventListenerGroup_INIT_COLLECTION.fireEventOnEachListener( event, InitializeCollectionEventListener::onInitializeCollection ); delayedAfterCompletion(); } From 91299aeb7faa703c67e72f4e9d4b7c3a1e61106e Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 20 Aug 2019 21:10:07 +0100 Subject: [PATCH 59/99] HHH-13565 Remove some dead code from SessionImpl --- .../org/hibernate/internal/SessionImpl.java | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 7b1a04c847..6743b4426c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -725,37 +725,15 @@ public final class SessionImpl // persistOnFlush() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - public void persistOnFlush(String entityName, Object object) - throws HibernateException { - firePersistOnFlush( new PersistEvent( entityName, object, this ) ); - } - - public void persistOnFlush(Object object) throws HibernateException { - persist( null, object ); - } - @Override - public void persistOnFlush(String entityName, Object object, Map copiedAlready) - throws HibernateException { - firePersistOnFlush( copiedAlready, new PersistEvent( entityName, object, this ) ); - } - - private void firePersistOnFlush(final Map copiedAlready, final PersistEvent event) { + public void persistOnFlush(String entityName, Object object, Map copiedAlready) { checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); + PersistEvent event = new PersistEvent( entityName, object, this ); fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, (l,e) -> l.onPersist( e, copiedAlready ) ); delayedAfterCompletion(); } - private void firePersistOnFlush(final PersistEvent event) { - checkOpen(); - checkTransactionSynchStatus(); - checkNoUnresolvedActionsBeforeOperation(); - fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, PersistEventListener::onPersist ); - checkNoUnresolvedActionsAfterOperation(); - } - - // merge() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override From cc39f54717f9c6761a108dc7c14ab77533cf8766 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 20 Aug 2019 21:18:38 +0100 Subject: [PATCH 60/99] HHH-13565 Micro cleanup of Trace level checks --- .../org/hibernate/internal/SessionImpl.java | 13 +++++++---- .../internal/ResultSetProcessorImpl.java | 23 ++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 6743b4426c..19130390b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -814,7 +814,9 @@ public final class SessionImpl public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities) throws HibernateException { checkOpenOrWaitingForAutoClose(); - if ( log.isTraceEnabled() && persistenceContext.isRemovingOrphanBeforeUpates() ) { + final boolean removingOrphanBeforeUpates = persistenceContext.isRemovingOrphanBeforeUpates(); + final boolean traceEnabled = log.isTraceEnabled(); + if ( traceEnabled && removingOrphanBeforeUpates ) { logRemoveOrphanBeforeUpdates( "before continuing", entityName, object ); } fireDelete( @@ -822,12 +824,12 @@ public final class SessionImpl entityName, object, isCascadeDeleteEnabled, - persistenceContext.isRemovingOrphanBeforeUpates(), + removingOrphanBeforeUpates, this ), transientEntities ); - if ( log.isTraceEnabled() && persistenceContext.isRemovingOrphanBeforeUpates() ) { + if ( traceEnabled && removingOrphanBeforeUpates ) { logRemoveOrphanBeforeUpdates( "after continuing", entityName, object ); } } @@ -836,7 +838,8 @@ public final class SessionImpl public void removeOrphanBeforeUpdates(String entityName, Object child) { // TODO: The removeOrphan concept is a temporary "hack" for HHH-6484. This should be removed once action/task // ordering is improved. - if ( log.isTraceEnabled() ) { + final boolean traceEnabled = log.isTraceEnabled(); + if ( traceEnabled ) { logRemoveOrphanBeforeUpdates( "begin", entityName, child ); } persistenceContext.beginRemoveOrphanBeforeUpdates(); @@ -846,7 +849,7 @@ public final class SessionImpl } finally { persistenceContext.endRemoveOrphanBeforeUpdates(); - if ( log.isTraceEnabled() ) { + if ( traceEnabled ) { logRemoveOrphanBeforeUpdates( "end", entityName, child ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java index c710b2e01c..81c32a0fae 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java @@ -82,13 +82,20 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session ); + final boolean traceEnabled = LOG.isTraceEnabled(); final int maxRows; + final List loadResults; final RowSelection selection = queryParameters.getRowSelection(); if ( LimitHelper.hasMaxRows( selection ) ) { maxRows = selection.getMaxRows(); - LOG.tracef( "Limiting ResultSet processing to just %s rows", maxRows ); + if ( traceEnabled ) { + LOG.tracef( "Limiting ResultSet processing to just %s rows", maxRows ); + } + int sizeHint = maxRows < 50 ? maxRows : 50; + loadResults = new ArrayList( sizeHint ); } else { + loadResults = new ArrayList(); maxRows = Integer.MAX_VALUE; } @@ -109,12 +116,14 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { hadSubselectFetches ); - final List loadResults = new ArrayList(); - - LOG.trace( "Processing result set" ); + if ( traceEnabled ) { + LOG.trace( "Processing result set" ); + } int count; for ( count = 0; count < maxRows && resultSet.next(); count++ ) { - LOG.debugf( "Starting ResultSet row #%s", count ); + if ( traceEnabled ) { + LOG.tracef( "Starting ResultSet row #%s", count ); + } Object logicalRow = rowReader.readRow( resultSet, context ); @@ -125,7 +134,9 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { context.finishUpRow(); } - LOG.tracev( "Done processing result set ({0} rows)", count ); + if ( traceEnabled ) { + LOG.tracev( "Done processing result set ({0} rows)", count ); + } rowReader.finishUp( context, afterLoadActionList ); context.wrapUp(); From 2de048fde5872d0149798513448802d77fea92c3 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 20 Aug 2019 21:59:16 +0100 Subject: [PATCH 61/99] HHH-13565 Prepare the most commonly needed SessionBuilder instances in advance --- .../hibernate/internal/SessionFactoryImpl.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 573863847f..c0176cffe1 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -198,6 +198,8 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { private final transient TypeHelper typeHelper; private final transient FastSessionServices fastSessionServices; + private final transient SessionBuilder defaultSessionOpenOptions; + private final transient SessionBuilder temporarySessionOpenOptions; public SessionFactoryImpl( final MetadataImplementor metadata, @@ -377,6 +379,11 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { fetchProfiles.put( fetchProfile.getName(), fetchProfile ); } + this.defaultSessionOpenOptions = withOptions(); + this.temporarySessionOpenOptions = withOptions() + .autoClose( false ) + .flushMode( FlushMode.MANUAL ) + .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT ); this.fastSessionServices = new FastSessionServices( this ); this.observer.sessionFactoryCreated( this ); @@ -474,15 +481,11 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor { } public Session openSession() throws HibernateException { - return withOptions().openSession(); + return this.defaultSessionOpenOptions.openSession(); } public Session openTemporarySession() throws HibernateException { - return withOptions() - .autoClose( false ) - .flushMode( FlushMode.MANUAL ) - .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT ) - .openSession(); + return this.temporarySessionOpenOptions.openSession(); } public Session getCurrentSession() throws HibernateException { From 646a8756a9cfa41425826d7cd6f9608a0b6c9e54 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 21 Aug 2019 09:16:38 +0100 Subject: [PATCH 62/99] HHH-13565 Making SessionFactoryImpl#LockOptions lazily initialized as well --- .../internal/FastSessionServices.java | 9 +++ .../org/hibernate/internal/SessionImpl.java | 75 ++++++------------ .../jpa/internal/util/LockOptionsHelper.java | 77 +++++++++++++++++++ 3 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockOptionsHelper.java diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index f3367e2701..e1688c3293 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -39,6 +39,7 @@ import org.hibernate.event.spi.SaveOrUpdateEventListener; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.QueryHints; import org.hibernate.jpa.internal.util.CacheModeHelper; +import org.hibernate.jpa.internal.util.LockOptionsHelper; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; @@ -118,6 +119,7 @@ final class FastSessionServices { final CacheMode initialSessionCacheMode; final boolean discardOnClose; final BaselineSessionEventsListenerBuilder defaultSessionEventListeners; + final LockOptions defaultLockOptions; //Private fields: private final Dialect dialect; @@ -174,6 +176,13 @@ final class FastSessionServices { this.discardOnClose = sessionFactoryOptions.isReleaseResourcesOnCloseEnabled(); this.defaultJdbcObservers = Collections.singletonList( new ConnectionObserverStatsBridge( sf ) ); this.defaultSessionEventListeners = sessionFactoryOptions.getBaselineSessionEventsListenerBuilder(); + this.defaultLockOptions = initializeDefaultLockOptions( defaultSessionProperties ); + } + + private static LockOptions initializeDefaultLockOptions(final Map defaultSessionProperties) { + LockOptions def = new LockOptions(); + LockOptionsHelper.applyPropertiesToLockOptions( defaultSessionProperties, () -> def ); + return def; } private static EventListenerGroup listeners(EventListenerRegistry elr, EventType type) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 19130390b8..c393a8ce53 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -145,6 +145,7 @@ import org.hibernate.jpa.internal.util.CacheModeHelper; import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.jpa.internal.util.LockOptionsHelper; import org.hibernate.jpa.spi.HibernateEntityManagerImplementor; import org.hibernate.loader.criteria.CriteriaLoader; import org.hibernate.loader.custom.CustomLoader; @@ -207,8 +208,7 @@ public final class SessionImpl private transient LoadQueryInfluencers loadQueryInfluencers; - // todo : (5.2) HEM always initialized this. Is that really needed? - private LockOptions lockOptions = new LockOptions(); + private LockOptions lockOptions; private boolean autoClear; private boolean autoClose; @@ -248,7 +248,10 @@ public final class SessionImpl statistics.openSession(); } - setLockOptions( this.properties == null ? fastSessionServices.defaultSessionProperties : this.properties, this.lockOptions ); + if ( this.properties != null ) { + //There might be custom properties for this session that affect the LockOptions state + LockOptionsHelper.applyPropertiesToLockOptions( this.properties, this::getLockOptionsForWrite ); + } getSession().setCacheMode( fastSessionServices.initialSessionCacheMode ); // NOTE : pulse() already handles auto-join-ability correctly @@ -259,9 +262,21 @@ public final class SessionImpl } } + private LockOptions getLockOptionsForRead() { + return this.lockOptions == null ? fastSessionServices.defaultLockOptions : this.lockOptions; + } + + private LockOptions getLockOptionsForWrite() { + if ( this.lockOptions == null ) { + this.lockOptions = new LockOptions(); + } + return this.lockOptions; + } + protected void applyQuerySettingsAndHints(Query query) { - if ( lockOptions.getLockMode() != LockMode.NONE ) { - query.setLockMode( getLockMode( lockOptions.getLockMode() ) ); + final LockOptions lockOptionsForRead = getLockOptionsForRead(); + if ( lockOptionsForRead.getLockMode() != LockMode.NONE ) { + query.setLockMode( getLockMode( lockOptionsForRead.getLockMode() ) ); } final Object queryTimeout; if ( ( queryTimeout = getSessionProperty( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) { @@ -3255,57 +3270,17 @@ public final class SessionImpl @Override public LockOptions getLockRequest(LockModeType lockModeType, Map properties) { LockOptions lockOptions = new LockOptions(); - LockOptions.copy( this.lockOptions, lockOptions ); + if ( this.lockOptions != null ) { //otherwise the default LockOptions constructor is the same as DEFAULT_LOCK_OPTIONS + LockOptions.copy( this.lockOptions, lockOptions ); + } lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) ); if ( properties != null ) { - setLockOptions( properties, lockOptions ); + LockOptionsHelper.applyPropertiesToLockOptions( properties, () -> lockOptions ); } return lockOptions; } - private void setLockOptions(Map props, LockOptions options) { - Object lockScope = props.get( JPA_LOCK_SCOPE ); - if ( lockScope instanceof String && PessimisticLockScope.valueOf( ( String ) lockScope ) == PessimisticLockScope.EXTENDED ) { - options.setScope( true ); - } - else if ( lockScope instanceof PessimisticLockScope ) { - boolean extended = PessimisticLockScope.EXTENDED.equals( lockScope ); - options.setScope( extended ); - } - else if ( lockScope != null ) { - throw new PersistenceException( "Unable to parse " + JPA_LOCK_SCOPE + ": " + lockScope ); - } - Object lockTimeout = props.get( JPA_LOCK_TIMEOUT ); - int timeout = 0; - boolean timeoutSet = false; - if ( lockTimeout instanceof String ) { - timeout = Integer.parseInt( ( String ) lockTimeout ); - timeoutSet = true; - } - else if ( lockTimeout instanceof Number ) { - timeout = ( (Number) lockTimeout ).intValue(); - timeoutSet = true; - } - else if ( lockTimeout != null ) { - throw new PersistenceException( "Unable to parse " + JPA_LOCK_TIMEOUT + ": " + lockTimeout ); - } - - if ( timeoutSet ) { - if ( timeout == LockOptions.SKIP_LOCKED ) { - options.setTimeOut( LockOptions.SKIP_LOCKED ); - } - else if ( timeout < 0 ) { - options.setTimeOut( LockOptions.WAIT_FOREVER ); - } - else if ( timeout == 0 ) { - options.setTimeOut( LockOptions.NO_WAIT ); - } - else { - options.setTimeOut( timeout ); - } - } - } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3572,7 +3547,7 @@ public final class SessionImpl setHibernateFlushMode( ConfigurationHelper.getFlushMode( value, FlushMode.AUTO ) ); } else if ( JPA_LOCK_SCOPE.equals( propertyName ) || JPA_LOCK_TIMEOUT.equals( propertyName ) ) { - setLockOptions( properties, this.lockOptions ); + LockOptionsHelper.applyPropertiesToLockOptions( properties, this::getLockOptionsForWrite ); } else if ( JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName ) || JPA_SHARED_CACHE_STORE_MODE.equals( propertyName ) ) { getSession().setCacheMode( diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockOptionsHelper.java b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockOptionsHelper.java new file mode 100644 index 0000000000..20e1ea641b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/jpa/internal/util/LockOptionsHelper.java @@ -0,0 +1,77 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpa.internal.util; + +import java.util.Map; +import java.util.function.Supplier; +import javax.persistence.PersistenceException; +import javax.persistence.PessimisticLockScope; + +import org.hibernate.LockOptions; + +import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_SCOPE; +import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT; + +public final class LockOptionsHelper { + + private LockOptionsHelper() { + //utility class, not to be constructed + } + + /** + * Applies configuration properties on a {@link LockOptions} instance, passed as a supplier + * so to make it possible to skip allocating the {@link LockOptions} instance if there's + * nothing to set. + * + * @param props The configuration properties + * @param lockOptionsSupplier The reference to the lock to modify + */ + public static void applyPropertiesToLockOptions(final Map props, final Supplier lockOptionsSupplier) { + Object lockScope = props.get( JPA_LOCK_SCOPE ); + if ( lockScope instanceof String && PessimisticLockScope.valueOf( (String) lockScope ) == PessimisticLockScope.EXTENDED ) { + lockOptionsSupplier.get().setScope( true ); + } + else if ( lockScope instanceof PessimisticLockScope ) { + boolean extended = PessimisticLockScope.EXTENDED.equals( lockScope ); + lockOptionsSupplier.get().setScope( extended ); + } + else if ( lockScope != null ) { + throw new PersistenceException( "Unable to parse " + JPA_LOCK_SCOPE + ": " + lockScope ); + } + + Object lockTimeout = props.get( JPA_LOCK_TIMEOUT ); + int timeout = 0; + boolean timeoutSet = false; + if ( lockTimeout instanceof String ) { + timeout = Integer.parseInt( (String) lockTimeout ); + timeoutSet = true; + } + else if ( lockTimeout instanceof Number ) { + timeout = ( (Number) lockTimeout ).intValue(); + timeoutSet = true; + } + else if ( lockTimeout != null ) { + throw new PersistenceException( "Unable to parse " + JPA_LOCK_TIMEOUT + ": " + lockTimeout ); + } + + if ( timeoutSet ) { + if ( timeout == LockOptions.SKIP_LOCKED ) { + lockOptionsSupplier.get().setTimeOut( LockOptions.SKIP_LOCKED ); + } + else if ( timeout < 0 ) { + lockOptionsSupplier.get().setTimeOut( LockOptions.WAIT_FOREVER ); + } + else if ( timeout == 0 ) { + lockOptionsSupplier.get().setTimeOut( LockOptions.NO_WAIT ); + } + else { + lockOptionsSupplier.get().setTimeOut( timeout ); + } + } + } + +} From 9bfffd85d7beeaee98630bb73dbbb3fb7a11f5e7 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 21 Aug 2019 11:48:18 +0100 Subject: [PATCH 63/99] HHH-13565 Ensure all events from EventListenerGroup can be fired without allocations --- .../internal/EventListenerGroupImpl.java | 95 +++++++++++++++---- .../service/spi/EventActionWithParameter.java | 17 ++++ .../event/service/spi/EventListenerGroup.java | 13 +++ .../org/hibernate/internal/SessionImpl.java | 12 +-- 4 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/event/service/spi/EventActionWithParameter.java diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java index 20e9d5c40a..3f4cc95fd7 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java @@ -6,15 +6,19 @@ */ package org.hibernate.event.service.internal; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Supplier; import org.hibernate.event.service.spi.DuplicationStrategy; +import org.hibernate.event.service.spi.EventActionWithParameter; import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.service.spi.EventListenerRegistrationException; import org.hibernate.event.service.spi.JpaBootstrapSensitive; @@ -23,6 +27,7 @@ import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; /** * @author Steve Ebersole + * @author Sanne Grinovero */ class EventListenerGroupImpl implements EventListenerGroup { private EventType eventType; @@ -30,9 +35,14 @@ class EventListenerGroupImpl implements EventListenerGroup { private final Set duplicationStrategies = new LinkedHashSet<>(); - // Performance: make sure a forEach iteration on this type is efficient; in particular we choose ArrayList - // so to avoid allocating iterators. - private ArrayList listeners; + // Performance: make sure iteration on this type is efficient; in particular we do not want to allocate iterators, + // not having to capture state in lambdas. + // So we keep the listeners in both a List (for convenience) and in an array (for iteration). Make sure + // their content stays in synch! + private T[] listeners = null; + + //Update both fields when making changes! + private List listenersAsList; public EventListenerGroupImpl(EventType eventType, EventListenerRegistryImpl listenerRegistry) { this.eventType = eventType; @@ -66,7 +76,8 @@ class EventListenerGroupImpl implements EventListenerGroup { @Override public int count() { - return listeners == null ? 0 : listeners.size(); + final T[] ls = listeners; + return ls == null ? 0 : ls.length; } @Override @@ -74,16 +85,16 @@ class EventListenerGroupImpl implements EventListenerGroup { if ( duplicationStrategies != null ) { duplicationStrategies.clear(); } - if ( listeners != null ) { - listeners.clear(); - } + listeners = null; + listenersAsList = null; } @Override public final void fireLazyEventOnEachListener(final Supplier eventSupplier, final BiConsumer actionOnEvent) { - if ( listeners != null && listeners.size() != 0 ) { + final T[] ls = listeners; + if ( ls != null && ls.length != 0 ) { final U event = eventSupplier.get(); - for ( T listener : this.listeners ) { + for ( T listener : ls ) { actionOnEvent.accept( listener, event ); } } @@ -91,13 +102,24 @@ class EventListenerGroupImpl implements EventListenerGroup { @Override public final void fireEventOnEachListener(final U event, final BiConsumer actionOnEvent) { - if ( listeners != null ) { - for ( T listener : this.listeners ) { + final T[] ls = listeners; + if ( ls != null ) { + for ( T listener : ls ) { actionOnEvent.accept( listener, event ); } } } + @Override + public void fireEventOnEachListener(final U event, final X parameter, final EventActionWithParameter actionOnEvent) { + final T[] ls = listeners; + if ( ls != null ) { + for ( T listener : ls ) { + actionOnEvent.applyEventToListener( listener, event, parameter ); + } + } + } + @Override public void addDuplicationStrategy(DuplicationStrategy strategy) { duplicationStrategies.add( strategy ); @@ -105,22 +127,44 @@ class EventListenerGroupImpl implements EventListenerGroup { /** * Implementation note: should be final for performance reasons. + * @deprecated this is not the most efficient way for iterating the event listeners. + * See {@link #fireEventOnEachListener(Object, BiConsumer)} and co. for better alternatives. */ @Override + @Deprecated public final Iterable listeners() { - return listeners == null ? Collections.EMPTY_LIST : listeners; + final List ls = listenersAsList; + return ls == null ? Collections.EMPTY_LIST : ls; } @Override @SafeVarargs public final void appendListeners(T... listeners) { + internalAppendListeners( listeners ); + checkForArrayRefresh(); + } + + private void checkForArrayRefresh() { + final List list = listenersAsList; + if ( this.listeners == null ) { + T[] a = (T[]) Array.newInstance( eventType.baseListenerInterface(), list.size() ); + listeners = list.toArray( a ); + } + } + + private void internalAppendListeners(T[] listeners) { for ( T listener : listeners ) { - appendListener( listener ); + internalAppendListener( listener ); } } @Override public void appendListener(T listener) { + internalAppendListener( listener ); + checkForArrayRefresh(); + } + + private void internalAppendListener(T listener) { if ( listenerShouldGetAdded( listener ) ) { internalAppend( listener ); } @@ -129,28 +173,39 @@ class EventListenerGroupImpl implements EventListenerGroup { @Override @SafeVarargs public final void prependListeners(T... listeners) { + internalPrependListeners( listeners ); + checkForArrayRefresh(); + } + + private void internalPrependListeners(T[] listeners) { for ( T listener : listeners ) { - prependListener( listener ); + internalPreprendListener( listener ); } } @Override public void prependListener(T listener) { + internalPreprendListener( listener ); + checkForArrayRefresh(); + } + + private void internalPreprendListener(T listener) { if ( listenerShouldGetAdded( listener ) ) { internalPrepend( listener ); } } private boolean listenerShouldGetAdded(T listener) { - if ( listeners == null ) { - listeners = new ArrayList<>(); + final List ts = listenersAsList; + if ( ts == null ) { + listenersAsList = new ArrayList<>(); return true; // no need to do de-dup checks } boolean doAdd = true; strategy_loop: for ( DuplicationStrategy strategy : duplicationStrategies ) { - final ListIterator itr = listeners.listIterator(); + final ListIterator itr = ts.listIterator(); while ( itr.hasNext() ) { final T existingListener = itr.next(); if ( strategy.areMatch( listener, existingListener ) ) { @@ -180,7 +235,8 @@ class EventListenerGroupImpl implements EventListenerGroup { private void internalPrepend(T listener) { checkAgainstBaseInterface( listener ); performInjections( listener ); - listeners.add( 0, listener ); + listenersAsList.add( 0, listener ); + listeners = null; //Marks it for refreshing } private void performInjections(T listener) { @@ -206,6 +262,7 @@ class EventListenerGroupImpl implements EventListenerGroup { private void internalAppend(T listener) { checkAgainstBaseInterface( listener ); performInjections( listener ); - listeners.add( listener ); + listenersAsList.add( listener ); + listeners = null; //Marks it for refreshing } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventActionWithParameter.java b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventActionWithParameter.java new file mode 100644 index 0000000000..e685b5293b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventActionWithParameter.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.service.spi; + +import org.hibernate.Incubating; + +@Incubating +@FunctionalInterface +public interface EventActionWithParameter { + + void applyEventToListener(T eventListener, U action, X param); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java index 1f4e823a84..213f6acb64 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerGroup.java @@ -7,9 +7,11 @@ package org.hibernate.event.service.spi; import java.io.Serializable; +import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; +import org.hibernate.Incubating; import org.hibernate.event.spi.EventType; /** @@ -35,6 +37,12 @@ public interface EventListenerGroup extends Serializable { public int count(); + /** + * @deprecated this is not the most efficient way for iterating the event listeners. + * See {@link #fireEventOnEachListener(Object, BiConsumer)} and its overloaded variants for better alternatives. + * @return + */ + @Deprecated public Iterable listeners(); /** @@ -67,6 +75,7 @@ public interface EventListenerGroup extends Serializable { * @param actionOnEvent * @param the kind of event */ + @Incubating void fireLazyEventOnEachListener(final Supplier eventSupplier, final BiConsumer actionOnEvent); /** @@ -76,6 +85,10 @@ public interface EventListenerGroup extends Serializable { * @param actionOnEvent * @param the kind of event */ + @Incubating void fireEventOnEachListener(final U event, final BiConsumer actionOnEvent); + @Incubating + void fireEventOnEachListener(final U event, X param, final EventActionWithParameter actionOnEvent); + } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index c393a8ce53..656feaa482 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -724,7 +724,7 @@ public final class SessionImpl try { //Uses a capturing lambda in this case as we need to carry the additional Map parameter: fastSessionServices.eventListenerGroup_PERSIST - .fireEventOnEachListener( event, (l, e) -> l.onPersist( e, copiedAlready ) ); + .fireEventOnEachListener( event, copiedAlready, PersistEventListener::onPersist ); } catch ( MappingException e ) { throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage() ) ) ; @@ -745,7 +745,7 @@ public final class SessionImpl checkOpenOrWaitingForAutoClose(); pulseTransactionCoordinator(); PersistEvent event = new PersistEvent( entityName, object, this ); - fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, (l,e) -> l.onPersist( e, copiedAlready ) ); + fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, copiedAlready, PersistEventListener::onPersist ); delayedAfterCompletion(); } @@ -793,7 +793,7 @@ public final class SessionImpl private void fireMerge(final Map copiedAlready, final MergeEvent event) { try { pulseTransactionCoordinator(); - fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, (l,e) -> l.onMerge( e, copiedAlready ) ); + fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, copiedAlready, MergeEventListener::onMerge ); } catch ( ObjectDeletedException sse ) { throw getExceptionConverter().convert( new IllegalArgumentException( sse ) ); @@ -904,7 +904,7 @@ public final class SessionImpl private void fireDelete(final DeleteEvent event, final Set transientEntities) { try{ pulseTransactionCoordinator(); - fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, (l,e) -> l.onDelete( e, transientEntities ) ); + fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, transientEntities, DeleteEventListener::onDelete ); } catch ( ObjectDeletedException sse ) { throw getExceptionConverter().convert( new IllegalArgumentException( sse ) ); @@ -1179,7 +1179,7 @@ public final class SessionImpl // it seems they prevent these hot methods from being inlined. private void fireLoadNoChecks(final LoadEvent event, final LoadType loadType) { pulseTransactionCoordinator(); - fastSessionServices.eventListenerGroup_LOAD.fireEventOnEachListener( event, (l,e) -> l.onLoad( e, loadType ) ); + fastSessionServices.eventListenerGroup_LOAD.fireEventOnEachListener( event, loadType, LoadEventListener::onLoad ); } private void fireResolveNaturalId(final ResolveNaturalIdEvent event) { @@ -1262,7 +1262,7 @@ public final class SessionImpl private void fireRefresh(final Map refreshedAlready, final RefreshEvent event) { try { pulseTransactionCoordinator(); - fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, (l,e) -> l.onRefresh( e, refreshedAlready ) ); + fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, refreshedAlready, RefreshEventListener::onRefresh ); } catch (RuntimeException e) { throw getExceptionConverter().convert( e ); From 618a502dacafb4d6d384a691d1c0e58d561d8094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 20 Aug 2019 11:47:01 +0200 Subject: [PATCH 64/99] HHH-13592 Test AutoFlushEvent#isFlushRequired --- .../events/AutoFlushEventListenerTest.java | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/events/AutoFlushEventListenerTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/events/AutoFlushEventListenerTest.java b/hibernate-core/src/test/java/org/hibernate/test/events/AutoFlushEventListenerTest.java new file mode 100644 index 0000000000..4999ffb2b8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/events/AutoFlushEventListenerTest.java @@ -0,0 +1,143 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.events; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.AutoFlushEvent; +import org.hibernate.event.spi.AutoFlushEventListener; +import org.hibernate.event.spi.EventType; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class AutoFlushEventListenerTest extends BaseCoreFunctionalTestCase { + + private TheListener listener = new TheListener(); + + @Test + public void testAutoFlushRequired() { + listener.events.clear(); + + Session s = openSession(); + s.beginTransaction(); + + s.persist( new Entity1() ); + assertEquals( 0, listener.events.size() ); + + // An entity of this type was persisted; a flush is required + session.createQuery( "select i from Entity1 i" ) + .setHibernateFlushMode( FlushMode.AUTO ) + .getResultList(); + assertEquals( 1, listener.events.size() ); + assertTrue( listener.events.get( 0 ).isFlushRequired() ); + + s.getTransaction().commit(); + assertEquals( 1, listener.events.size() ); + s.close(); + assertEquals( 1, listener.events.size() ); + } + + @Test + public void testAutoFlushNotRequired() { + listener.events.clear(); + + Session s = openSession(); + s.beginTransaction(); + + s.persist( new Entity2() ); + assertEquals( 0, listener.events.size() ); + + // No entity of this type was persisted; no flush is required + session.createQuery( "select i from Entity1 i" ) + .setHibernateFlushMode( FlushMode.AUTO ) + .getResultList(); + assertEquals( 1, listener.events.size() ); + assertFalse( listener.events.get( 0 ).isFlushRequired() ); + + s.getTransaction().commit(); + assertEquals( 1, listener.events.size() ); + s.close(); + assertEquals( 1, listener.events.size() ); + } + + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + super.prepareBootstrapRegistryBuilder( builder ); + builder.applyIntegrator( + new Integrator() { + @Override + public void integrate( + Metadata metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + serviceRegistry.getService( EventListenerRegistry.class ).appendListeners( + EventType.AUTO_FLUSH, + listener + ); + } + + @Override + public void disintegrate(SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + } + } + ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Entity1.class, Entity2.class }; + } + + @Entity(name = "Entity1") + static class Entity1 { + @Id + @GeneratedValue + private Integer id; + + public Entity1() { + } + } + + @Entity(name = "Entity2") + static class Entity2 { + @Id + @GeneratedValue + private Integer id; + + public Entity2() { + } + } + + private static class TheListener implements AutoFlushEventListener { + private List events = new ArrayList<>(); + + @Override + public void onAutoFlush(AutoFlushEvent event) throws HibernateException { + events.add( event ); + } + } +} From 5fc6012449e80da5fe253a968bc2b5eb65b519c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 20 Aug 2019 11:47:19 +0200 Subject: [PATCH 65/99] HHH-13592 Populate AutoFlushEvent#isFlushRequired with the correct value --- .../event/internal/DefaultAutoFlushEventListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java index 3683bbfd9f..ec90f240fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java @@ -50,6 +50,7 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener flushEverythingToExecutions( event ); if ( flushIsReallyNeeded( event, source ) ) { LOG.trace( "Need to execute flush" ); + event.setFlushRequired( true ); // note: performExecutions() clears all collectionXxxxtion // collections (the collection actions) in the session @@ -65,10 +66,9 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener } else { LOG.trace( "Don't need to execute flush" ); + event.setFlushRequired( false ); actionQueue.clearFromFlushNeededCheck( oldSize ); } - - event.setFlushRequired( flushIsReallyNeeded( event, source ) ); } } finally { From 13afce9afbbad93acecfde023743a2024ae13824 Mon Sep 17 00:00:00 2001 From: Elena Felder <41136058+elefeint@users.noreply.github.com> Date: Fri, 24 May 2019 17:11:10 -0400 Subject: [PATCH 66/99] HHH-13412 Move custom driver properties into their own section --- .../userguide/chapters/jdbc/Database_Access.adoc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc index 049e8e280f..e5eb26478b 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc @@ -39,6 +39,13 @@ The `DataSource` `ConnectionProvider` also (optionally) accepts the `hibernate.c If specified, the https://docs.oracle.com/javase/8/docs/api/javax/sql/DataSource.html#getConnection-java.lang.String-java.lang.String-[`DataSource#getConnection(String username, String password)`] will be used. Otherwise, the no-arg form is used. +[[database-connectionprovider-driver]] +=== Driver Configuration +`hibernate.connection.driver_class`:: The name of the JDBC Driver class to use +`hibernate.connection.url`:: The JDBC connection url +Any settings prefixed with `hibernate.connection.` (other than the "special ones"):: All such setting names will have the `hibernate.connection.` prefix stripped. The remaining name and the original value will be passed to the driver as a JDBC connection property + + [[database-connectionprovider-c3p0]] === Using c3p0 @@ -48,13 +55,11 @@ To use the c3p0 integration, the application must include the `hibernate-c3p0` m ==== Hibernate also provides support for applications to use http://www.mchange.com/projects/c3p0/[c3p0] connection pooling. -When using this c3p0 support, a number of additional configuration settings are recognized. +When c3p0 support is enabled, a number of c3p0-specific configuration settings are recognized in addition to the general ones described in <>. Transaction isolation of the Connections is managed by the `ConnectionProvider` itself. See <>. -`hibernate.connection.driver_class`:: The name of the JDBC Driver class to use -`hibernate.connection.url`:: The JDBC connection url. -Any settings prefixed with `hibernate.connection.` (other than the "special ones"):: These all have the `hibernate.connection.` prefix stripped and the rest will be passed as JDBC connection properties + `hibernate.c3p0.min_size` or `c3p0.minPoolSize`:: The minimum size of the c3p0 pool. See http://www.mchange.com/projects/c3p0/#minPoolSize[c3p0 minPoolSize] `hibernate.c3p0.max_size` or `c3p0.maxPoolSize`:: The maximum size of the c3p0 pool. See http://www.mchange.com/projects/c3p0/#maxPoolSize[c3p0 maxPoolSize] `hibernate.c3p0.timeout` or `c3p0.maxIdleTime`:: The Connection idle time. See http://www.mchange.com/projects/c3p0/#maxIdleTime[c3p0 maxIdleTime] From ba1f15553c424f9f71dd35cf85689b757f59471b Mon Sep 17 00:00:00 2001 From: Elena Felder <41136058+elefeint@users.noreply.github.com> Date: Fri, 24 May 2019 17:29:09 -0400 Subject: [PATCH 67/99] HHH-13412 Remove stray newlines --- .../main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc index e5eb26478b..5be8b9baae 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc @@ -45,7 +45,6 @@ Otherwise, the no-arg form is used. `hibernate.connection.url`:: The JDBC connection url Any settings prefixed with `hibernate.connection.` (other than the "special ones"):: All such setting names will have the `hibernate.connection.` prefix stripped. The remaining name and the original value will be passed to the driver as a JDBC connection property - [[database-connectionprovider-c3p0]] === Using c3p0 @@ -59,7 +58,6 @@ When c3p0 support is enabled, a number of c3p0-specific configuration settings a Transaction isolation of the Connections is managed by the `ConnectionProvider` itself. See <>. - `hibernate.c3p0.min_size` or `c3p0.minPoolSize`:: The minimum size of the c3p0 pool. See http://www.mchange.com/projects/c3p0/#minPoolSize[c3p0 minPoolSize] `hibernate.c3p0.max_size` or `c3p0.maxPoolSize`:: The maximum size of the c3p0 pool. See http://www.mchange.com/projects/c3p0/#maxPoolSize[c3p0 maxPoolSize] `hibernate.c3p0.timeout` or `c3p0.maxIdleTime`:: The Connection idle time. See http://www.mchange.com/projects/c3p0/#maxIdleTime[c3p0 maxIdleTime] From 952bf6f9c0f85b30ba7fe6345bad581959f5d7e8 Mon Sep 17 00:00:00 2001 From: Elena Felder <41136058+elefeint@users.noreply.github.com> Date: Tue, 28 May 2019 12:49:33 -0400 Subject: [PATCH 68/99] HHH-13412 Added link for predefined properties, warning that not all settings apply to all situations --- .../asciidoc/userguide/chapters/jdbc/Database_Access.adoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc index 5be8b9baae..190c9b23e5 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/jdbc/Database_Access.adoc @@ -43,7 +43,9 @@ Otherwise, the no-arg form is used. === Driver Configuration `hibernate.connection.driver_class`:: The name of the JDBC Driver class to use `hibernate.connection.url`:: The JDBC connection url -Any settings prefixed with `hibernate.connection.` (other than the "special ones"):: All such setting names will have the `hibernate.connection.` prefix stripped. The remaining name and the original value will be passed to the driver as a JDBC connection property +`hibernate.connection.*`:: All such setting names (except the <>) will have the `hibernate.connection.` prefix stripped. The remaining name and the original value will be passed to the driver as a JDBC connection property + +NOTE: Not all properties apply to all situations. For example, if you are providing a data source, `hibernate.connection.driver_class` setting will not be used. [[database-connectionprovider-c3p0]] === Using c3p0 From c94117cedccc107730f7fc6a98242f6ea691a7fd Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 23 Aug 2019 15:10:02 +0100 Subject: [PATCH 69/99] HHH-13595 Deprecate ConnectionObserver --- .../hibernate/internal/FastSessionServices.java | 6 +++--- .../hibernate/internal/JdbcObserverImpl.java | 17 +++++------------ .../resource/jdbc/spi/JdbcObserver.java | 3 +++ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index e1688c3293..0557705813 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -125,7 +125,7 @@ final class FastSessionServices { private final Dialect dialect; private final CacheStoreMode defaultCacheStoreMode; private final CacheRetrieveMode defaultCacheRetrieveMode; - private List defaultJdbcObservers; + private final ConnectionObserverStatsBridge defaultJdbcObservers; FastSessionServices(SessionFactoryImpl sf) { Objects.requireNonNull( sf ); @@ -174,7 +174,7 @@ final class FastSessionServices { this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties ); this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode ); this.discardOnClose = sessionFactoryOptions.isReleaseResourcesOnCloseEnabled(); - this.defaultJdbcObservers = Collections.singletonList( new ConnectionObserverStatsBridge( sf ) ); + this.defaultJdbcObservers = new ConnectionObserverStatsBridge( sf ); this.defaultSessionEventListeners = sessionFactoryOptions.getBaselineSessionEventsListenerBuilder(); this.defaultLockOptions = initializeDefaultLockOptions( defaultSessionProperties ); } @@ -273,7 +273,7 @@ final class FastSessionServices { return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); } - public Iterable getDefaultJdbcObservers() { + public ConnectionObserverStatsBridge getDefaultJdbcObserver() { return defaultJdbcObservers; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java index be3cf12978..01f2eb71b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java @@ -8,7 +8,6 @@ package org.hibernate.internal; import java.sql.Connection; -import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.resource.jdbc.spi.JdbcObserver; @@ -18,11 +17,11 @@ import org.hibernate.resource.jdbc.spi.JdbcObserver; public class JdbcObserverImpl implements JdbcObserver { private final SharedSessionContractImplementor session; - private final Iterable observers; + private final ConnectionObserverStatsBridge observer; public JdbcObserverImpl(SharedSessionContractImplementor session, FastSessionServices fastSessionServices) { this.session = session; - this.observers = fastSessionServices.getDefaultJdbcObservers(); + this.observer = fastSessionServices.getDefaultJdbcObserver(); } @Override @@ -32,9 +31,7 @@ public class JdbcObserverImpl implements JdbcObserver { @Override public void jdbcConnectionAcquisitionEnd(Connection connection) { - for ( ConnectionObserver observer : observers ) { - observer.physicalConnectionObtained( connection ); - } + observer.physicalConnectionObtained( connection ); } @Override @@ -44,9 +41,7 @@ public class JdbcObserverImpl implements JdbcObserver { @Override public void jdbcConnectionReleaseEnd() { - for ( ConnectionObserver observer : observers ) { - observer.physicalConnectionReleased(); - } + observer.physicalConnectionReleased(); } @Override @@ -56,9 +51,7 @@ public class JdbcObserverImpl implements JdbcObserver { @Override public void jdbcPrepareStatementEnd() { - for ( ConnectionObserver observer : observers ) { - observer.statementPrepared(); - } + observer.statementPrepared(); session.getEventListenerManager().jdbcPrepareStatementEnd(); } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcObserver.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcObserver.java index 49aa95db79..124d28921d 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcObserver.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcObserver.java @@ -9,8 +9,11 @@ package org.hibernate.resource.jdbc.spi; import java.sql.Connection; /** + * @deprecated It is no longer possible to plug custom implementations of + * this SPI. It will be removed. * @author Steve Ebersole */ +@Deprecated public interface JdbcObserver { public void jdbcConnectionAcquisitionStart(); public void jdbcConnectionAcquisitionEnd(Connection connection); From db92f7aa00ff32dee1c59dbb3ee764241f51d582 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 23 Aug 2019 15:16:52 +0100 Subject: [PATCH 70/99] HHH-13595 Small tuning of JdbcObserverImpl --- .../hibernate/internal/JdbcObserverImpl.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java index 01f2eb71b4..015aca4385 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/JdbcObserverImpl.java @@ -8,25 +8,27 @@ package org.hibernate.internal; import java.sql.Connection; +import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.resource.jdbc.spi.JdbcObserver; /** * @author Steve Ebersole */ -public class JdbcObserverImpl implements JdbcObserver { +public final class JdbcObserverImpl implements JdbcObserver { - private final SharedSessionContractImplementor session; private final ConnectionObserverStatsBridge observer; + private final SessionEventListenerManager eventListenerManager; + private final SharedSessionContractImplementor session; public JdbcObserverImpl(SharedSessionContractImplementor session, FastSessionServices fastSessionServices) { this.session = session; this.observer = fastSessionServices.getDefaultJdbcObserver(); + this.eventListenerManager = session.getEventListenerManager(); } @Override public void jdbcConnectionAcquisitionStart() { - } @Override @@ -36,7 +38,6 @@ public class JdbcObserverImpl implements JdbcObserver { @Override public void jdbcConnectionReleaseStart() { - } @Override @@ -46,33 +47,33 @@ public class JdbcObserverImpl implements JdbcObserver { @Override public void jdbcPrepareStatementStart() { - session.getEventListenerManager().jdbcPrepareStatementStart(); + eventListenerManager.jdbcPrepareStatementStart(); } @Override public void jdbcPrepareStatementEnd() { observer.statementPrepared(); - session.getEventListenerManager().jdbcPrepareStatementEnd(); + eventListenerManager.jdbcPrepareStatementEnd(); } @Override public void jdbcExecuteStatementStart() { - session.getEventListenerManager().jdbcExecuteStatementStart(); + eventListenerManager.jdbcExecuteStatementStart(); } @Override public void jdbcExecuteStatementEnd() { - session.getEventListenerManager().jdbcExecuteStatementEnd(); + eventListenerManager.jdbcExecuteStatementEnd(); } @Override public void jdbcExecuteBatchStart() { - session.getEventListenerManager().jdbcExecuteBatchStart(); + eventListenerManager.jdbcExecuteBatchStart(); } @Override public void jdbcExecuteBatchEnd() { - session.getEventListenerManager().jdbcExecuteBatchEnd(); + eventListenerManager.jdbcExecuteBatchEnd(); } @Override From 44c6f0fa3f52a2959a6525421954c68b0b34b042 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 23 Aug 2019 15:20:02 +0100 Subject: [PATCH 71/99] HHH-13595 Remove the invocations to the no-op methods of ConnectionObserver --- .../resource/jdbc/internal/LogicalConnectionManagedImpl.java | 3 --- .../resource/jdbc/internal/ResourceRegistryStandardImpl.java | 4 ---- 2 files changed, 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java index 2a4cb84d5d..3486a39fc2 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java @@ -100,7 +100,6 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple private Connection acquireConnectionIfNeeded() { if ( physicalConnection == null ) { // todo : is this the right place for these observer calls? - observer.jdbcConnectionAcquisitionStart(); try { physicalConnection = jdbcConnectionAccess.obtainConnection(); } @@ -186,8 +185,6 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple return; } - // todo : is this the right place for these observer calls? - observer.jdbcConnectionReleaseStart(); try { if ( !physicalConnection.isClosed() ) { sqlExceptionHelper.logAndClearWarnings( physicalConnection ); diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java index 492a53c811..3f3af3b97f 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/ResourceRegistryStandardImpl.java @@ -361,10 +361,6 @@ public final class ResourceRegistryStandardImpl implements ResourceRegistry { } ); nclobs = null; } - - if ( jdbcObserver != null ) { - jdbcObserver.jdbcReleaseRegistryResourcesEnd(); - } } private boolean hasRegistered(final HashMap resource) { From 5c95096e7cc10a23f50e9654851e3b9fc6ee692a Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 23 Aug 2019 15:04:52 -0400 Subject: [PATCH 72/99] HHH-13564 - Fix NullPointerException for audited entity with embedded-id in mapping superclass that makes use of generics. --- .../entities/mapper/id/EmbeddedIdMapper.java | 4 +- .../embeddedid/EmbeddedIdGenericsTest.java | 219 ++++++++++++++++++ 2 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java index ed62002ccd..abbc84872a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java @@ -16,7 +16,6 @@ import java.util.Map; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.tools.ReflectionTools; -import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Setter; import org.hibernate.service.ServiceRegistry; @@ -73,11 +72,10 @@ public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements Simpl new PrivilegedAction() { @Override public Boolean run() { - final Getter getter = ReflectionTools.getGetter( obj.getClass(), idPropertyData, getServiceRegistry() ); final Setter setter = ReflectionTools.getSetter( obj.getClass(), idPropertyData, getServiceRegistry() ); try { - final Object subObj = ReflectHelper.getDefaultConstructor( getter.getReturnType() ).newInstance(); + final Object subObj = instantiateCompositeId(); boolean ret = true; for ( IdMapper idMapper : ids.values() ) { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java new file mode 100644 index 0000000000..4f68b065a6 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java @@ -0,0 +1,219 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.ids.embeddedid; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * Tests an entity mapping that uses an {@link EmbeddedId} mapping that makes use of generics. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-13564") +public class EmbeddedIdGenericsTest extends BaseEnversJPAFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { NotificationType.class, Trigger.class }; + } + + @Test + @Priority(10) + public void initData() { + // Revision 1 + // Store NotificationType and Trigger instance + doInJPA( this::entityManagerFactory, entityManager -> { + final NotificationType type = new NotificationType( "code" ); + entityManager.persist( type ); + + Trigger trigger = new Trigger( "str", type ); + entityManager.persist( trigger ); + + trigger.setActive( !trigger.isActive() ); + entityManager.merge( trigger ); + } ); + } + + @Test + public void testAuditQueryMappedSuperclassWithEmbeddedId() { + // There should be at least one revision for Trigger + List resultList = getAuditReader().createQuery().forRevisionsOfEntity( Trigger.class, true, true ).getResultList(); + assertEquals( 1, resultList.size() ); + + // Trigger should be hydrated with a composite-id values below + Trigger entityInstance = (Trigger) resultList.get( 0 ); + assertEquals( "str", entityInstance.getPk().getEventType() ); + assertEquals( "code", entityInstance.getPk().getNotificationType().getCode() ); + } + + + @MappedSuperclass + public abstract static class CompositeIdBaseEntity implements Serializable { + protected PK pk; + + @EmbeddedId + public PK getPk() { + return pk; + } + + public void setPk(PK pk) { + this.pk = pk; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + CompositeIdBaseEntity that = (CompositeIdBaseEntity) o; + return Objects.equals( pk, that.pk ); + } + + @Override + public int hashCode() { + return Objects.hash( pk ); + } + } + + @Audited + @Entity(name = "Trigger") + public static class Trigger extends CompositeIdBaseEntity { + private boolean active; + + Trigger() { + + } + + public Trigger(String eventType, NotificationType notificationType) { + this.pk = new TriggerPK( eventType, notificationType ); + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Embeddable + public static class TriggerPK implements Serializable { + private String eventType; + private NotificationType notificationType; + + TriggerPK() { + + } + + public TriggerPK(String eventType, NotificationType notificationType) { + this.eventType = eventType; + this.notificationType = notificationType; + } + + @Column(nullable = false, insertable = false, updatable = false) + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @ManyToOne + @JoinColumn(insertable = false, updatable = false, nullable = false) + public NotificationType getNotificationType() { + return notificationType; + } + + public void setNotificationType(NotificationType notificationType) { + this.notificationType = notificationType; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + TriggerPK triggerPK = (TriggerPK) o; + return Objects.equals( eventType, triggerPK.eventType ) && + Objects.equals( notificationType, triggerPK.notificationType ); + } + + @Override + public int hashCode() { + return Objects.hash( eventType, notificationType ); + } + } + } + + @Entity(name = "NotificationType") + public static class NotificationType { + @Id + private String code; + + public NotificationType() { + + } + + public NotificationType(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + NotificationType that = (NotificationType) o; + return Objects.equals( code, that.code ); + } + + @Override + public int hashCode() { + return Objects.hash( code ); + } + } +} From bcbfea7724a8f2dd14d56e9a7708d4e9680b591a Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 27 Aug 2019 18:11:13 +0100 Subject: [PATCH 73/99] HHH-13599 Avoid ArrayList allocation in JtaTransactionCoordinatorImp in common scenario --- .../JtaTransactionCoordinatorImpl.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java index b2f3a59eae..13f2edaba5 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java @@ -62,7 +62,7 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy private int timeOut = -1; - private final transient List observers; + private transient List observers = null; /** * Construct a JtaTransactionCoordinatorImpl instance. package-protected to ensure access goes through @@ -79,8 +79,6 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy this.transactionCoordinatorOwner = owner; this.autoJoinTransactions = autoJoinTransactions; - this.observers = new ArrayList<>(); - final JdbcSessionContext jdbcSessionContext = owner.getJdbcSessionOwner().getJdbcSessionContext(); this.jtaPlatform = jdbcSessionContext.getServiceRegistry().getService( JtaPlatform.class ); @@ -109,9 +107,8 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy this.preferUserTransactions = preferUserTransactions; this.performJtaThreadTracking = performJtaThreadTracking; - this.observers = new ArrayList<>(); - if ( observers != null ) { + this.observers = new ArrayList<>( observers.length ); Collections.addAll( this.observers, observers ); } @@ -123,11 +120,17 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy /** * Needed because while iterating the observers list and executing the before/update callbacks, * some observers might get removed from the list. + * Yet try to not allocate anything for when the list is empty, as this is a common case. * * @return TransactionObserver */ private Iterable observers() { - return new ArrayList<>( observers ); + if ( this.observers == null ) { + return Collections.EMPTY_LIST; + } + else { + return new ArrayList<>( this.observers ); + } } public SynchronizationCallbackCoordinator getSynchronizationCallbackCoordinator() { @@ -388,12 +391,17 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy } public void addObserver(TransactionObserver observer) { + if ( this.observers == null ) { + this.observers = new ArrayList<>( 3 ); //These lists are typically very small. + } observers.add( observer ); } @Override public void removeObserver(TransactionObserver observer) { - observers.remove( observer ); + if ( observers != null ) { + observers.remove( observer ); + } } /** From 3d715c0ae8879a1cdc202f52ee3f0cc6c582e005 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 28 Aug 2019 11:08:01 +0100 Subject: [PATCH 74/99] HHH-13600 Extract Alias allocation from loop in AbstractCollectionPersister --- .../persister/collection/AbstractCollectionPersister.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 42380d1fe7..e7f9e2de1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -1915,8 +1915,9 @@ public abstract class AbstractCollectionPersister } String[] result = new String[rawAliases.length]; + final Alias alias = new Alias( suffix ); for ( int i = 0; i < rawAliases.length; i++ ) { - result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] ); + result[i] = alias.toUnquotedAliasString( rawAliases[i] ); } return result; } From 86e498f4f7bd4b2f8ecc6f02d57844f344209c60 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 28 Aug 2019 11:15:09 +0100 Subject: [PATCH 75/99] HHH-13600 Avoid capturing lambdas in AbstractDomainDataRegion construction --- .../spi/support/AbstractDomainDataRegion.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/support/AbstractDomainDataRegion.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/support/AbstractDomainDataRegion.java index a76eacfc1c..568757c580 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/support/AbstractDomainDataRegion.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/support/AbstractDomainDataRegion.java @@ -7,8 +7,9 @@ package org.hibernate.cache.spi.support; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.hibernate.cache.CacheException; import org.hibernate.cache.cfg.spi.CollectionDataCachingConfig; @@ -121,15 +122,16 @@ public abstract class AbstractDomainDataRegion extends AbstractRegion implements private Map generateEntityDataAccessMap( DomainDataRegionConfig regionConfig) { - if ( regionConfig.getEntityCaching().isEmpty() ) { + final List entityCaching = regionConfig.getEntityCaching(); + if ( entityCaching.isEmpty() ) { return Collections.emptyMap(); } - final Map accessMap = new ConcurrentHashMap<>(); - for ( EntityDataCachingConfig entityAccessConfig : regionConfig.getEntityCaching() ) { - accessMap.computeIfAbsent( + final Map accessMap = new HashMap<>( entityCaching.size() ); + for ( EntityDataCachingConfig entityAccessConfig : entityCaching ) { + accessMap.put( entityAccessConfig.getNavigableRole(), - hierarchy -> generateEntityAccess( entityAccessConfig ) + generateEntityAccess( entityAccessConfig ) ); } @@ -137,15 +139,16 @@ public abstract class AbstractDomainDataRegion extends AbstractRegion implements } private Map generateNaturalIdDataAccessMap(DomainDataRegionConfig regionConfig) { - if ( regionConfig.getNaturalIdCaching().isEmpty() ) { + final List naturalIdCaching = regionConfig.getNaturalIdCaching(); + if ( naturalIdCaching.isEmpty() ) { return Collections.emptyMap(); } - final Map accessMap = new ConcurrentHashMap<>(); - for ( NaturalIdDataCachingConfig naturalIdAccessConfig : regionConfig.getNaturalIdCaching() ) { - accessMap.computeIfAbsent( + final Map accessMap = new HashMap<>( naturalIdCaching.size() ); + for ( NaturalIdDataCachingConfig naturalIdAccessConfig : naturalIdCaching ) { + accessMap.put( naturalIdAccessConfig.getNavigableRole(), - hierarchy -> generateNaturalIdAccess( naturalIdAccessConfig ) + generateNaturalIdAccess( naturalIdAccessConfig ) ); } @@ -154,15 +157,16 @@ public abstract class AbstractDomainDataRegion extends AbstractRegion implements private Map generateCollectionDataAccessMap( DomainDataRegionConfig regionConfig) { - if ( regionConfig.getCollectionCaching().isEmpty() ) { + final List collectionCaching = regionConfig.getCollectionCaching(); + if ( collectionCaching.isEmpty() ) { return Collections.emptyMap(); } - final Map accessMap = new ConcurrentHashMap<>(); - for ( CollectionDataCachingConfig cachingConfig : regionConfig.getCollectionCaching() ) { - accessMap.computeIfAbsent( + final Map accessMap = new HashMap<>( collectionCaching.size() ); + for ( CollectionDataCachingConfig cachingConfig : collectionCaching ) { + accessMap.put( cachingConfig.getNavigableRole(), - hierarchy -> generateCollectionAccess( cachingConfig ) + generateCollectionAccess( cachingConfig ) ); } From 9f71bd890d32bbcc02e0e8572cb632a8975e938c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 28 Aug 2019 11:21:04 +0100 Subject: [PATCH 76/99] HHH-13600 Unguarded log statement in CollectionCacheInvalidator --- .../hibernate/cache/internal/CollectionCacheInvalidator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java index e3d35c0b7e..81793fb23d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java @@ -111,6 +111,7 @@ public class CollectionCacheInvalidator return; } final EntityMetamodel entityMetamodel = persister.getEntityMetamodel(); + final boolean debugEnabled = LOG.isDebugEnabled(); for ( String role : collectionRoles ) { final CollectionPersister collectionPersister = metamodel.collectionPersister( role ); if ( !collectionPersister.hasCache() ) { @@ -142,7 +143,9 @@ public class CollectionCacheInvalidator } } else { - LOG.debug( "Evict CollectionRegion " + role ); + if ( debugEnabled ) { + LOG.debug( "Evict CollectionRegion " + role ); + } final CollectionDataAccess cacheAccessStrategy = collectionPersister.getCacheAccessStrategy(); final SoftLock softLock = cacheAccessStrategy.lockRegion(); session.getActionQueue().registerProcess( (success, session1) -> { From c8a36cd961464797c6277449d7f640af05c89e8d Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 28 Aug 2019 11:26:18 +0100 Subject: [PATCH 77/99] HHH-13600 Extracting some more constants from loops --- .../hql/internal/ast/exec/DeleteExecutor.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java index 849e5c7991..a904220098 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java @@ -17,6 +17,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.hql.internal.ast.HqlSqlWalker; import org.hibernate.hql.internal.ast.SqlGenerator; import org.hibernate.hql.internal.ast.tree.DeleteStatement; +import org.hibernate.metamodel.spi.MetamodelImplementor; import org.hibernate.param.ParameterSpecification; import org.hibernate.persister.collection.AbstractCollectionPersister; import org.hibernate.persister.entity.Queryable; @@ -63,15 +64,18 @@ public class DeleteExecutor extends BasicExecutor { parameterSpecifications = new ArrayList<>(); idSubselectWhere = ""; } - + + final boolean commentsEnabled = factory.getSessionFactoryOptions().isCommentsEnabled(); + final MetamodelImplementor metamodel = factory.getMetamodel(); + final boolean notSupportingTuplesInSubqueries = !dialect.supportsTuplesInSubqueries(); // If many-to-many, delete the FK row in the collection table. for ( Type type : persister.getPropertyTypes() ) { if ( type.isCollectionType() ) { final CollectionType cType = (CollectionType) type; - final AbstractCollectionPersister cPersister = (AbstractCollectionPersister) factory.getMetamodel().collectionPersister( cType.getRole() ); + final AbstractCollectionPersister cPersister = (AbstractCollectionPersister) metamodel.collectionPersister( cType.getRole() ); if ( cPersister.isManyToMany() ) { if ( persister.getIdentifierColumnNames().length > 1 - && !dialect.supportsTuplesInSubqueries() ) { + && notSupportingTuplesInSubqueries ) { LOG.warn( "This dialect is unable to cascade the delete into the many-to-many join table" + " when the entity has multiple primary keys. Either properly setup cascading on" + @@ -85,7 +89,7 @@ public class DeleteExecutor extends BasicExecutor { final String where = "(" + String.join( ", ", cPersister.getKeyColumnNames() ) + ") in " + idSubselect; final Delete delete = new Delete().setTableName( cPersister.getTableName() ).setWhere( where ); - if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) { + if ( commentsEnabled ) { delete.setComment( "delete FKs in join table" ); } deletes.add( delete.toStatementString() ); From 40b30fa0998e521f68c837dc897e53b4f75f87b2 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 28 Aug 2019 11:39:46 +0100 Subject: [PATCH 78/99] HHH-13600 Avoid capturing lambdas in ParameterTranslationsImpl --- .../ast/ParameterTranslationsImpl.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/ParameterTranslationsImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/ParameterTranslationsImpl.java index efefa8c72b..c430dc604e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/ParameterTranslationsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/ParameterTranslationsImpl.java @@ -48,10 +48,7 @@ public class ParameterTranslationsImpl implements ParameterTranslations { } final PositionalParameterSpecification ordinalSpecification = (PositionalParameterSpecification) specification; - final PositionalParameterInformationImpl info = ordinalParameters.computeIfAbsent( - ordinalSpecification.getLabel(), - k -> new PositionalParameterInformationImpl( k, ordinalSpecification.getExpectedType() ) - ); + final PositionalParameterInformationImpl info = getPositionalParameterInfo( ordinalParameters, ordinalSpecification ); info.addSourceLocation( i++ ); } else if ( NamedParameterSpecification.class.isInstance( specification ) ) { @@ -60,10 +57,7 @@ public class ParameterTranslationsImpl implements ParameterTranslations { } final NamedParameterSpecification namedSpecification = (NamedParameterSpecification) specification; - final NamedParameterInformationImpl info = namedParameters.computeIfAbsent( - namedSpecification.getName(), - k -> new NamedParameterInformationImpl( k, namedSpecification.getExpectedType() ) - ); + final NamedParameterInformationImpl info = getNamedParameterInfo( namedParameters, namedSpecification ); /* If a previous reference to the NamedParameter already exists with expected type null and the new @@ -96,6 +90,30 @@ public class ParameterTranslationsImpl implements ParameterTranslations { } } + private NamedParameterInformationImpl getNamedParameterInfo( + Map namedParameters, + NamedParameterSpecification namedSpecification) { + final String name = namedSpecification.getName(); + NamedParameterInformationImpl namedParameterInformation = namedParameters.get( name ); + if ( namedParameterInformation == null ) { + namedParameterInformation = new NamedParameterInformationImpl( name, namedSpecification.getExpectedType() ); + namedParameters.put( name, namedParameterInformation ); + } + return namedParameterInformation; + } + + private static PositionalParameterInformationImpl getPositionalParameterInfo( + Map ordinalParameters, + PositionalParameterSpecification ordinalSpecification) { + final Integer label = Integer.valueOf( ordinalSpecification.getLabel() ); + PositionalParameterInformationImpl positionalParameterInformation = ordinalParameters.get( label ); + if ( positionalParameterInformation == null ) { + positionalParameterInformation = new PositionalParameterInformationImpl( label, ordinalSpecification.getExpectedType() ); + ordinalParameters.put( label, positionalParameterInformation ); + } + return positionalParameterInformation; + } + @Override @SuppressWarnings("unchecked") public Map getNamedParameterInformationMap() { From ea301a7084fbbde81b749e64908369aa382d0962 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 2 Sep 2019 13:52:29 +0100 Subject: [PATCH 79/99] HHH-13564 - Fix EmbeddedIdGenericsTest failures on Oracle and MariaDB --- .../test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java index 4f68b065a6..67de748ab9 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/embeddedid/EmbeddedIdGenericsTest.java @@ -18,6 +18,7 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MappedSuperclass; +import javax.persistence.Table; import org.hibernate.envers.Audited; import org.hibernate.envers.RelationTargetAuditMode; @@ -105,6 +106,7 @@ public class EmbeddedIdGenericsTest extends BaseEnversJPAFunctionalTestCase { @Audited @Entity(name = "Trigger") + @Table(name = "`Trigger`") public static class Trigger extends CompositeIdBaseEntity { private boolean active; From 11409ed633e62f507d54d8a819dd6a3326afe982 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Sun, 25 Aug 2019 22:31:50 -0700 Subject: [PATCH 80/99] HHH-13590 : test cases --- ...cadeMergeToProxyEntityCopyAllowedTest.java | 315 ++++++++++++++++++ .../CascadeMergeToProxySimpleTest.java | 167 ++++++++++ 2 files changed, 482 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxyEntityCopyAllowedTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxySimpleTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxyEntityCopyAllowedTest.java b/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxyEntityCopyAllowedTest.java new file mode 100644 index 0000000000..0493ca0bca --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxyEntityCopyAllowedTest.java @@ -0,0 +1,315 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cascade; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToMany; +import javax.persistence.Transient; +import javax.persistence.TypedQuery; +import javax.persistence.Version; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@TestForIssue( jiraKey = "HHH-13590") +public class CascadeMergeToProxyEntityCopyAllowedTest extends BaseCoreFunctionalTestCase { + private Project defaultProject; + + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + AbstractEntity.class, + Event.class, + Project.class, + Speaker.class + }; + } + + @Test + public void test() { + final Event root = (Event) persistEntity( new Event( null, defaultProject ) ); + + Event rootFromDB = doInHibernate( + this::sessionFactory, session -> { + TypedQuery eventTypedQuery = session.createQuery( + "SELECT e FROM Event e LEFT JOIN FETCH e.speakers LEFT JOIN FETCH e.children LEFT JOIN FETCH e.project WHERE e.objectID = :oid", + Event.class + ); + + eventTypedQuery.setParameter( "oid", root.getObjectID() ); + + return eventTypedQuery.getSingleResult(); + + } + ); + assertNotNull( rootFromDB ); + assertEquals(0, rootFromDB.getChildren().size()); + assertEquals( 0, rootFromDB.getSpeakers().size() ); + assertEquals( root, rootFromDB ); + + Speaker speaker = (Speaker) persistEntity( new Speaker(defaultProject) ); + final long speakerId = speaker.getObjectID(); + + speaker = doInHibernate( + this::sessionFactory, session -> { + return session.find( Speaker.class, speakerId ); + } + ); + assertNotNull( speaker ); + + Event child = new Event(rootFromDB, defaultProject); + child.addSpeaker( speaker ); + + rootFromDB = (Event) persistEntity( rootFromDB ); + final long rootFromDBId = rootFromDB.getObjectID(); + rootFromDB = doInHibernate( + this::sessionFactory, session -> { + TypedQuery eventTypedQuery = session.createQuery( + "SELECT e FROM Event e LEFT JOIN FETCH e.speakers LEFT JOIN FETCH e.children LEFT JOIN FETCH e.project WHERE e.objectID = :oid", + Event.class + ); + + eventTypedQuery.setParameter( "oid", rootFromDBId ); + + return eventTypedQuery.getSingleResult(); + + } + ); + assertNotNull( rootFromDB ); + assertEquals(1, rootFromDB.getChildren().size()); + assertEquals(0, rootFromDB.getSpeakers().size()); + + } + + private Object persistEntity(Object entity ) { + return doInHibernate( + this::sessionFactory, session -> { + Object mergedEntity = session.merge( entity ); + session.persist( mergedEntity ); + session.flush(); + return mergedEntity; + } + ); + } + + @Override + protected void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( AvailableSettings.MERGE_ENTITY_COPY_OBSERVER, "allow" ); + } + + @Before + public void setupData() { + Long objectId = doInHibernate( + this::sessionFactory, session -> { + Project project = (Project) session.merge( new Project() ); + session.persist( project ); + session.flush(); + return project.getObjectID(); + } + ); + doInHibernate( + this::sessionFactory, session -> { + TypedQuery projectTypedQuery = session.createQuery("SELECT p FROM Project p WHERE p.objectID = :oid", Project.class); + + projectTypedQuery.setParameter("oid", objectId); + + defaultProject = projectTypedQuery.getSingleResult(); + } + ); + } + + @MappedSuperclass + public static class AbstractEntity { + + static long INVALID_OBJECT_ID = -1 ; + + @Transient + protected static final Random RANDOM_GENERATOR = new Random(); + + @Id + @GeneratedValue + @Column(name = "id") + protected Long objectID = INVALID_OBJECT_ID; + + @Version + private int version; + + @Column(nullable = false, unique = true, length = 36) + private final String bID; + + protected AbstractEntity() { + bID = UUID.nameUUIDFromBytes( + ( Long.toString( System.currentTimeMillis() ) + RANDOM_GENERATOR.nextInt() ) + .getBytes() + ).toString(); + } + + public int getVersion() { + return version; + } + + public long getObjectID() { + return objectID; + } + + public static boolean isValidObjectID(long id) { + return (id > 0 && id != AbstractEntity.INVALID_OBJECT_ID); + } + + public boolean isPersistent() { + return isValidObjectID(getObjectID()); + } + + @Override + public String toString() { + return String.format("%s[id=%d]", getClass().getSimpleName(), getObjectID()); + } + + public String getBID() { + return bID; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractEntity that = (AbstractEntity) o; + + return bID != null ? bID.equals(that.bID) : that.bID == null; + + } + + @Override + public int hashCode() { + return bID != null ? bID.hashCode() : 0; + } + } + + @Entity(name = "Event") + @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) + public static class Event extends AbstractEntity { + + @ManyToOne(targetEntity = Event.class, fetch = FetchType.EAGER) + private Event parent; + + @OneToMany(targetEntity = Event.class, cascade = { CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "parent") + private Set children = new HashSet<>(); + + @ManyToOne(targetEntity = Project.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) + private Project project; + + @ManyToMany(targetEntity = Speaker.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REFRESH}) + private Set speakers = new HashSet<>(); + + + public Event() { + //framework purpose + } + + public Event(Event parent, Project project) { + setParent(parent); + setProject(project); + + if (parent == null) { + //nothing to do here, Event has no parent + } else { + parent.addChild(this); + } + } + + public void setParent(Event parent) { + this.parent = parent; + } + + public void setProject(Project project) { + this.project = project; + } + + public Event getParent() { + return parent; + } + + public Project getProject() { + return project; + } + + public Set getSpeakers() { + return Collections.unmodifiableSet( speakers ); + } + + public Set getChildren() { + return Collections.unmodifiableSet( children ); + } + + public void addSpeaker(Speaker speaker) { + assert speaker != null; + this.speakers.add(speaker); + } + + public void addChild(Event event) { + assert event != null; + this.children.add(event); + event.setParent(this); + } + + } + + @Entity(name = "Project") + @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) + public static class Project extends AbstractEntity { + + } + + @Entity(name = "Speaker") + @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) + public static class Speaker extends AbstractEntity { + + + @ManyToOne(targetEntity = Project.class, fetch = FetchType.LAZY, + cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) + private Project project; + + public Speaker() { + + } + + public Speaker(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxySimpleTest.java b/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxySimpleTest.java new file mode 100644 index 0000000000..972ad2aebc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cascade/CascadeMergeToProxySimpleTest.java @@ -0,0 +1,167 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cascade; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToMany; + +import org.hibernate.cfg.Configuration; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertNotNull; + +@TestForIssue( jiraKey = "HHH-13590") +public class CascadeMergeToProxySimpleTest extends BaseCoreFunctionalTestCase { + protected static final Random RANDOM_GENERATOR = new Random(); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + AbstractEntity.class, + Event.class, + Project.class + }; + } + + @Test + public void test() { + final Event root = (Event) mergeEntity( new Event( generateBId(), new Project( generateBId() ) ) ); + Event rootFromDB = (Event) mergeEntity( root ); + + assertNotNull( rootFromDB ); + } + + private Object mergeEntity(Object entity) { + return doInHibernate( + this::sessionFactory, session -> { + return session.merge( entity ); + } + ); + } + + private String generateBId() { + return UUID.nameUUIDFromBytes( + ( Long.toString( System.currentTimeMillis() ) + RANDOM_GENERATOR.nextInt() ) + .getBytes() + ).toString(); + } + + @Override + protected void configure(Configuration cfg) { + super.configure( cfg ); + } + + @MappedSuperclass + public static class AbstractEntity { + + @Id + @GeneratedValue + @Column(name = "id") + protected Long objectID; + + @Column(nullable = false, unique = true, length = 36) + private String bID; + + protected AbstractEntity() { + } + + protected AbstractEntity(String bId) { + this.bID = bId; + } + public long getObjectID() { + return objectID; + } + + @Override + public String toString() { + return String.format("%s[id=%d]", getClass().getSimpleName(), getObjectID()); + } + + public String getBID() { + return bID; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ( o == null || !this.getClass().isInstance( o ) ) return false; + + AbstractEntity that = (AbstractEntity) o; + + return bID != null ? bID.equals( that.bID) : that.bID == null; + } + + @Override + public int hashCode() { + return bID != null ? bID.hashCode() : 0; + } + } + + @Entity(name = "Event") + public static class Event extends AbstractEntity { + + @OneToMany(targetEntity = Event.class, cascade = { CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.LAZY) + private Set children = new HashSet<>(); + + @ManyToOne(targetEntity = Project.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) + private Project project; + + public Event() { + //framework purpose + } + + public Event(String bid, Project project) { + super( bid ); + setProject(project); + } + + public void setProject(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + + public Set getChildren() { + return children; + } + + public void addChild(Event event) { + assert event != null; + this.children.add(event); + } + + } + + @Entity(name = "Project") + public static class Project extends AbstractEntity { + + Project() { + } + + Project(String bId) { + super( bId ); + } + + } +} From 148b4b2ed009b488206b8e3854bc7270dc7931b4 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Sun, 25 Aug 2019 22:32:31 -0700 Subject: [PATCH 81/99] HHH-13590 : TransientObjectException merging a non-proxy association to a proxy --- .../org/hibernate/event/internal/DefaultMergeEventListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 56326d172a..39f1f47b76 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -359,7 +359,7 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme Object managed, EntityPersister persister, EventSource source) { - if ( incoming instanceof HibernateProxy ) { + if ( managed instanceof HibernateProxy ) { return source.getPersistenceContextInternal().unproxy( managed ); } From 650b1dec6a105a40f197cececd754f24cdca7e57 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Fri, 30 Aug 2019 19:02:44 -0700 Subject: [PATCH 82/99] HHH-13607 : Added tests --- ...turalIdInUninitializedAssociationTest.java | 173 ++++++++++++++++++ .../NaturalIdInUninitializedProxyTest.java | 150 +++++++++++++++ ...turalIdInUninitializedAssociationTest.java | 173 ++++++++++++++++++ .../NaturalIdInUninitializedProxyTest.java | 144 +++++++++++++++ 4 files changed, 640 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/NaturalIdInUninitializedAssociationTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/NaturalIdInUninitializedProxyTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedAssociationTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedProxyTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/NaturalIdInUninitializedAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/NaturalIdInUninitializedAssociationTest.java new file mode 100644 index 0000000000..1f86b4aa83 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/NaturalIdInUninitializedAssociationTest.java @@ -0,0 +1,173 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.LazyToOne; +import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.annotations.NaturalId; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Gail Badner + */ +@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"}) +@TestForIssue( jiraKey = "HHH-13607" ) +@RunWith( BytecodeEnhancerRunner.class ) +@EnhancementOptions( lazyLoading = true ) +public class NaturalIdInUninitializedAssociationTest extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testImmutableNaturalId() { + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertFalse( Hibernate.isPropertyInitialized( e,"entityImmutableNaturalId" ) ); + } + ); + + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertEquals( "immutable name", e.entityImmutableNaturalId.name ); + } + ); + } + + @Test + public void testMutableNaturalId() { + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertFalse( Hibernate.isPropertyInitialized( e,"entityMutableNaturalId" ) ); + } + ); + + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertEquals( "mutable name", e.entityMutableNaturalId.name ); + } + ); + } + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( AnEntity.class ); + sources.addAnnotatedClass( EntityMutableNaturalId.class ); + sources.addAnnotatedClass( EntityImmutableNaturalId.class ); + } + + @Before + public void prepareTestData() { + inTransaction( + session -> { + EntityMutableNaturalId entityMutableNaturalId = new EntityMutableNaturalId( 1, "mutable name" ); + EntityImmutableNaturalId entityImmutableNaturalId = new EntityImmutableNaturalId( 2, "immutable name" ); + AnEntity anEntity = new AnEntity(); + anEntity.id = 3; + anEntity.entityImmutableNaturalId = entityImmutableNaturalId; + anEntity.entityMutableNaturalId = entityMutableNaturalId; + session.persist( anEntity ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.delete( session.get( AnEntity.class, 3 ) ); + } + ); + } + + @Entity(name = "AnEntity") + public static class AnEntity { + @Id + private int id; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @LazyToOne(LazyToOneOption.NO_PROXY ) + private EntityMutableNaturalId entityMutableNaturalId; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @LazyToOne(LazyToOneOption.NO_PROXY ) + private EntityImmutableNaturalId entityImmutableNaturalId; + } + + @Entity(name = "EntityMutableNaturalId") + public static class EntityMutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = true) + private String name; + + public EntityMutableNaturalId() { + } + + public EntityMutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + + @Entity(name = "EntityImmutableNaturalId") + public static class EntityImmutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = false) + private String name; + + public EntityImmutableNaturalId() { + } + + public EntityImmutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/NaturalIdInUninitializedProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/NaturalIdInUninitializedProxyTest.java new file mode 100644 index 0000000000..37f677804e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/NaturalIdInUninitializedProxyTest.java @@ -0,0 +1,150 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.NaturalId; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Gail Badner + */ +@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"}) +@TestForIssue( jiraKey = "HHH-13607" ) +@RunWith( BytecodeEnhancerRunner.class ) +@EnhancementOptions( lazyLoading = true ) +public class NaturalIdInUninitializedProxyTest extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testImmutableNaturalId() { + inTransaction( + session -> { + final EntityImmutableNaturalId e = session.getReference( EntityImmutableNaturalId.class, 1 ); + assertFalse( Hibernate.isInitialized( e ) ); + } + ); + + inTransaction( + session -> { + final EntityImmutableNaturalId e = session.get( EntityImmutableNaturalId.class, 1 ); + assertEquals( "name", e.name ); + } + ); + } + + @Test + public void testMutableNaturalId() { + inTransaction( + session -> { + final EntityMutableNaturalId e = session.getReference( EntityMutableNaturalId.class, 1 ); + assertFalse( Hibernate.isInitialized( e ) ); + } + ); + + inTransaction( + session -> { + final EntityMutableNaturalId e = session.get( EntityMutableNaturalId.class, 1 ); + assertEquals( "name", e.name ); + } + ); + } + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( EntityMutableNaturalId.class ); + sources.addAnnotatedClass( EntityImmutableNaturalId.class ); + } + + @Before + public void prepareTestData() { + inTransaction( + session -> { + session.persist( new EntityMutableNaturalId( 1, "name" ) ); + session.persist( new EntityImmutableNaturalId( 1, "name" ) ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from EntityMutableNaturalId" ).executeUpdate(); + session.createQuery( "delete from EntityImmutableNaturalId" ).executeUpdate(); + } + ); + } + + @Entity(name = "EntityMutableNaturalId") + public static class EntityMutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = true) + private String name; + + public EntityMutableNaturalId() { + } + + public EntityMutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + + @Entity(name = "EntityImmutableNaturalId") + public static class EntityImmutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = false) + private String name; + + public EntityImmutableNaturalId() { + } + + public EntityImmutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedAssociationTest.java new file mode 100644 index 0000000000..f17d4baed4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedAssociationTest.java @@ -0,0 +1,173 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.naturalid.lazy; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.NaturalId; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Gail Badner + */ +@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"}) +@TestForIssue( jiraKey = "HHH-13607" ) +public class NaturalIdInUninitializedAssociationTest extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testImmutableNaturalId() { + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertFalse( Hibernate.isInitialized( e.entityImmutableNaturalId ) ); + } + ); + + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + Hibernate.initialize( e.entityImmutableNaturalId ); + assertEquals( "immutable name", e.entityImmutableNaturalId.getName() ); + } + ); + } + + @Test + public void testMutableNaturalId() { + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertFalse( Hibernate.isInitialized( e.entityMutableNaturalId ) ); + } + ); + + inTransaction( + session -> { + final AnEntity e = session.get( AnEntity.class, 3 ); + assertEquals( "mutable name", e.entityMutableNaturalId.getName() ); + } + ); + } + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( AnEntity.class ); + sources.addAnnotatedClass( EntityMutableNaturalId.class ); + sources.addAnnotatedClass( EntityImmutableNaturalId.class ); + } + + @Before + public void prepareTestData() { + inTransaction( + session -> { + EntityMutableNaturalId entityMutableNaturalId = new EntityMutableNaturalId( 1, "mutable name" ); + EntityImmutableNaturalId entityImmutableNaturalId = new EntityImmutableNaturalId( 2, "immutable name" ); + AnEntity anEntity = new AnEntity(); + anEntity.id = 3; + anEntity.entityImmutableNaturalId = entityImmutableNaturalId; + anEntity.entityMutableNaturalId = entityMutableNaturalId; + session.persist( anEntity ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.delete( session.get( AnEntity.class, 3 ) ); + } + ); + } + + @Entity(name = "AnEntity") + public static class AnEntity { + @Id + private int id; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private EntityMutableNaturalId entityMutableNaturalId; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private EntityImmutableNaturalId entityImmutableNaturalId; + } + + @Entity(name = "EntityMutableNaturalId") + public static class EntityMutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = true) + private String name; + + public EntityMutableNaturalId() { + } + + public EntityMutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + + public String getName() { + return name; + } + } + + @Entity(name = "EntityImmutableNaturalId") + public static class EntityImmutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = false) + private String name; + + public EntityImmutableNaturalId() { + } + + public EntityImmutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + + public String getName() { + return name; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedProxyTest.java new file mode 100644 index 0000000000..b73ce854f7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/lazy/NaturalIdInUninitializedProxyTest.java @@ -0,0 +1,144 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.naturalid.lazy; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.NaturalId; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Gail Badner + */ +@SuppressWarnings({"unused", "WeakerAccess","ResultOfMethodCallIgnored"}) +@TestForIssue( jiraKey = "HHH-13607" ) +public class NaturalIdInUninitializedProxyTest extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testImmutableNaturalId() { + inTransaction( + session -> { + final EntityImmutableNaturalId e = session.getReference( EntityImmutableNaturalId.class, 1 ); + assertFalse( Hibernate.isInitialized( e ) ); + } + ); + + inTransaction( + session -> { + final EntityImmutableNaturalId e = session.get( EntityImmutableNaturalId.class, 1 ); + assertEquals( "name", e.name ); + } + ); + } + + @Test + public void testMutableNaturalId() { + inTransaction( + session -> { + final EntityMutableNaturalId e = session.getReference( EntityMutableNaturalId.class, 1 ); + assertFalse( Hibernate.isInitialized( e ) ); + } + ); + + inTransaction( + session -> { + final EntityMutableNaturalId e = session.get( EntityMutableNaturalId.class, 1 ); + assertEquals( "name", e.name ); + } + ); + } + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.FORMAT_SQL, "false" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( EntityMutableNaturalId.class ); + sources.addAnnotatedClass( EntityImmutableNaturalId.class ); + } + + @Before + public void prepareTestData() { + inTransaction( + session -> { + session.persist( new EntityMutableNaturalId( 1, "name" ) ); + session.persist( new EntityImmutableNaturalId( 1, "name" ) ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from EntityMutableNaturalId" ).executeUpdate(); + session.createQuery( "delete from EntityImmutableNaturalId" ).executeUpdate(); + } + ); + } + + @Entity(name = "EntityMutableNaturalId") + public static class EntityMutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = true) + private String name; + + public EntityMutableNaturalId() { + } + + public EntityMutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + + @Entity(name = "EntityImmutableNaturalId") + public static class EntityImmutableNaturalId { + @Id + private int id; + + @NaturalId(mutable = false) + private String name; + + public EntityImmutableNaturalId() { + } + + public EntityImmutableNaturalId(int id, String name) { + this.id = id; + this.name = name; + } + } + +} From 7ceaf3aaa42992b18f809adc2322388a81018fc2 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 4 Sep 2019 16:42:10 -0500 Subject: [PATCH 83/99] HHH-13607 - Exception thrown while flushing uninitialized enhanced proxy with immutable natural ID --- .../DefaultFlushEntityEventListener.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java index 1e60d6a5d7..1f0a604902 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java @@ -17,11 +17,14 @@ import org.hibernate.StaleObjectStateException; import org.hibernate.action.internal.DelayedPostInsertIdentifier; import org.hibernate.action.internal.EntityUpdateAction; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.engine.internal.Nullability; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; @@ -84,14 +87,24 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener private void checkNaturalId( EntityPersister persister, + Object entity, EntityEntry entry, Object[] current, Object[] loaded, SessionImplementor session) { + if ( entity instanceof PersistentAttributeInterceptable ) { + final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { + // EARLY EXIT!!! + // nothing to check - the entity is an un-initialized enhancement-as-proxy reference + return; + } + } + if ( persister.hasNaturalIdentifier() && entry.getStatus() != Status.READ_ONLY ) { if ( !persister.getEntityMetamodel().hasImmutableNaturalId() ) { - // SHORT-CUT: if the natural id is mutable (!immutable), no need to do the below checks // EARLY EXIT!!! + // the natural id is mutable (!immutable), no need to do the below checks return; } @@ -115,7 +128,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener if ( !propertyType.isEqual( current[naturalIdentifierPropertyIndex], snapshot[i] ) ) { throw new HibernateException( String.format( - "An immutable natural identifier of entity %s was altered from %s to %s", + "An immutable natural identifier of entity %s was altered from `%s` to `%s`", persister.getEntityName(), propertyTypes[naturalIdentifierPropertyIndex].toLoggableString( snapshot[i], @@ -191,7 +204,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener // grab its current state values = persister.getPropertyValues( entity ); - checkNaturalId( persister, entry, values, loadedState, session ); + checkNaturalId( persister, entity, entry, values, loadedState, session ); } return values; } From 1cb81c0304b6ed8f521054245abf69ab03fb3357 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Fri, 23 Aug 2019 22:39:50 -0700 Subject: [PATCH 84/99] HHH-13586 : test case --- .../SharedDomainDataAndQueryResultsTest.java | 435 ++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/cache/SharedDomainDataAndQueryResultsTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/SharedDomainDataAndQueryResultsTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/SharedDomainDataAndQueryResultsTest.java new file mode 100644 index 0000000000..5de7a14cb6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cache/SharedDomainDataAndQueryResultsTest.java @@ -0,0 +1,435 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cache; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedQuery; +import javax.persistence.QueryHint; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cache.spi.CacheImplementor; +import org.hibernate.cache.spi.DomainDataRegion; +import org.hibernate.cache.spi.QueryResultsCache; +import org.hibernate.cache.spi.Region; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.stat.CacheRegionStatistics; +import org.hibernate.stat.QueryStatistics; +import org.hibernate.stat.SecondLevelCacheStatistics; +import org.hibernate.stat.Statistics; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.cache.CachingRegionFactory; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class SharedDomainDataAndQueryResultsTest extends BaseNonConfigCoreFunctionalTestCase { + + private static final String QUERY = "SELECT a FROM Dog a"; + private static final String REGION = "TheRegion"; + private static final String PREFIX = "test"; + + @Test + @TestForIssue( jiraKey = "HHH-13586") + public void testAllCachedStatistics() { + + final Statistics statistics = sessionFactory().getStatistics(); + statistics.clear(); + + final SecondLevelCacheStatistics regionStatisticsDeprecated = statistics.getSecondLevelCacheStatistics( + PREFIX + '.' + REGION + ); + final CacheRegionStatistics regionStatistics = statistics.getCacheRegionStatistics( REGION ); + assertSame( regionStatistics, regionStatisticsDeprecated ); + + final QueryStatistics queryStatistics = statistics.getQueryStatistics( QUERY ); + + doInHibernate( + this::sessionFactory, session -> { + + Dog yogi = session.get( Dog.class, "Yogi" ); + + assertEquals( 1, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 0, statistics.getSecondLevelCachePutCount() ); + assertEquals( 0, statistics.getSecondLevelCacheMissCount() ); + + assertEquals( 1, regionStatistics.getHitCount() ); + assertEquals( 0, regionStatistics.getPutCount() ); + assertEquals( 0, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 0, statistics.getQueryCachePutCount() ); + assertEquals( 0, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 0, queryStatistics.getCachePutCount() ); + assertEquals( 0, queryStatistics.getCacheMissCount() ); + + assertFalse( Hibernate.isInitialized( yogi.nickNames ) ); + Hibernate.initialize( yogi.nickNames ); + + assertEquals( 1, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 1, statistics.getSecondLevelCachePutCount() ); + assertEquals( 1, statistics.getSecondLevelCacheMissCount() ); + + assertEquals( 1, regionStatistics.getHitCount() ); + assertEquals( 1, regionStatistics.getPutCount() ); + assertEquals( 1, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 0, statistics.getQueryCachePutCount() ); + assertEquals( 0, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 0, queryStatistics.getCachePutCount() ); + assertEquals( 0, queryStatistics.getCacheMissCount() ); + } + ); + + doInHibernate( + this::sessionFactory, session -> { + + Dog yogi = session.get( Dog.class, "Yogi" ); + + assertEquals( 2, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 1, statistics.getSecondLevelCachePutCount() ); + assertEquals( 1, statistics.getSecondLevelCacheMissCount() ); + + assertEquals( 2, regionStatistics.getHitCount() ); + assertEquals( 1, regionStatistics.getPutCount() ); + assertEquals( 1, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 0, statistics.getQueryCachePutCount() ); + assertEquals( 0, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 0, queryStatistics.getCachePutCount() ); + assertEquals( 0, queryStatistics.getCacheMissCount() ); + + assertFalse( Hibernate.isInitialized( yogi.nickNames ) ); + Hibernate.initialize( yogi.nickNames ); + + assertEquals( 3, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 1, statistics.getSecondLevelCachePutCount() ); + assertEquals( 1, statistics.getSecondLevelCacheMissCount() ); + + assertEquals( 3, regionStatistics.getHitCount() ); + assertEquals( 1, regionStatistics.getPutCount() ); + assertEquals( 1, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 0, statistics.getQueryCachePutCount() ); + assertEquals( 0, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 0, queryStatistics.getCachePutCount() ); + assertEquals( 0, queryStatistics.getCacheMissCount() ); + + } + ); + + doInHibernate( + this::sessionFactory, session -> { + + List dogs = session.createNamedQuery( "Dog.findAll", Dog.class ).list(); + + assertEquals( 2, dogs.size() ); + + // statistics.getSecondLevelCacheHitCount() only includes entity/collection hits + assertEquals( 3, statistics.getSecondLevelCacheHitCount() ); + // statistics.getSecondLevelCachePutCount() only includes entity/collection puts + assertEquals( 3, statistics.getSecondLevelCachePutCount() ); + // statistics.getSecondLevelCacheMissCount() only includes entity/collection misses + assertEquals( 1, statistics.getSecondLevelCacheMissCount() ); + + // regionStatistics has hits/puts/misses for entities/collections/query results + // Query results missed and put in cache. + // Reference caching is not being used, so entities are not looked up in the cache. + // Entities get put in cache. + assertEquals( 3, regionStatistics.getHitCount() ); + // 2 Dog puts; 1 query put + assertEquals( 4, regionStatistics.getPutCount() ); + // 1 query miss + assertEquals( 2, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 1, queryStatistics.getCachePutCount() ); + assertEquals( 1, queryStatistics.getCacheMissCount() ); + + for ( Dog dog : dogs ) { + assertFalse( Hibernate.isInitialized( dog.nickNames ) ); + Hibernate.initialize( dog.nickNames ); + } + + // yogi.nickNames will be found in the cache as a cache hit. + // The other collection will be a cache miss and will be put in the cache + // statistics.getSecondLevelCacheHitCount() only includes entity/collection hits + assertEquals( 4, statistics.getSecondLevelCacheHitCount() ); + // statistics.getSecondLevelCachePutCount() only includes entity/collection puts + assertEquals( 4, statistics.getSecondLevelCachePutCount() ); + // statistics.getSecondLevelCacheMissCount() only includes entity/collection misses + assertEquals( 2, statistics.getSecondLevelCacheMissCount() ); + + // regionStatistics includes hits/puts/misses for entities/collections/query results + assertEquals( 4, regionStatistics.getHitCount() ); + // 2 Dog puts; 1 query put + assertEquals( 5, regionStatistics.getPutCount() ); + // 1 query miss + assertEquals( 3, regionStatistics.getMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + assertEquals( 0, queryStatistics.getCacheHitCount() ); + assertEquals( 1, queryStatistics.getCachePutCount() ); + assertEquals( 1, queryStatistics.getCacheMissCount() ); + + } + ); + + doInHibernate( + this::sessionFactory, session -> { + + List dogs = session.getNamedQuery( "Dog.findAll" ).list(); + + assertEquals( 2, dogs.size() ); + + // statistics.getSecondLevelCacheHitCount() only includes entity/collection hits + assertEquals( 6, statistics.getSecondLevelCacheHitCount() ); + // statistics.getSecondLevelCachePutCount() only includes entity/collection puts + assertEquals( 4, statistics.getSecondLevelCachePutCount() ); + // statistics.getSecondLevelCacheMissCount() only includes entity/collection misses + assertEquals( 2, statistics.getSecondLevelCacheMissCount() ); + + // regionStatistics includes hits/puts/misses for entities/collections/query results + // Query results will be found in the cache. + // The 2 Dog entities will be found in the cache. + assertEquals( 7, regionStatistics.getHitCount() ); + assertEquals( 5, regionStatistics.getPutCount() ); + assertEquals( 3, regionStatistics.getMissCount() ); + + assertEquals( 1, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + assertEquals( 1, queryStatistics.getCacheHitCount() ); + assertEquals( 1, queryStatistics.getCachePutCount() ); + assertEquals( 1, queryStatistics.getCacheMissCount() ); + + for ( Dog dog : dogs ) { + assertFalse( Hibernate.isInitialized( dog.nickNames ) ); + Hibernate.initialize( dog.nickNames ); + } + + // Both Dog.nickNames will be found in the cache as a cache hit. + + // statistics.getSecondLevelCacheHitCount() only includes entity/collection hits + assertEquals( 8, statistics.getSecondLevelCacheHitCount() ); + // statistics.getSecondLevelCachePutCount() only includes entity/collection puts + assertEquals( 4, statistics.getSecondLevelCachePutCount() ); + // statistics.getSecondLevelCacheMissCount() only includes entity/collection misses + assertEquals( 2, statistics.getSecondLevelCacheMissCount() ); + + // regionStatistics includes hits/puts/misses for entities/collections/query results + assertEquals( 9, regionStatistics.getHitCount() ); + // 2 Dog puts; 1 query put + assertEquals( 5, regionStatistics.getPutCount() ); + // 1 query miss + assertEquals( 3, regionStatistics.getMissCount() ); + + assertEquals( 1, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + assertEquals( 1, queryStatistics.getCacheHitCount() ); + assertEquals( 1, queryStatistics.getCachePutCount() ); + assertEquals( 1, queryStatistics.getCacheMissCount() ); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-13586") + public void testCacheImplementorGetRegion() { + rebuildSessionFactory(); + + final CacheImplementor cache = sessionFactory().getCache(); + final Region domainDataRegion = cache.getRegion( REGION ); + assertTrue( DomainDataRegion.class.isInstance( domainDataRegion ) ); + assertEquals( REGION, domainDataRegion.getName() ); + + // There should not be a QueryResultsRegion named REGION until + // the named query is executed. + assertNull( cache.getQueryResultsCacheStrictly( REGION ) ); + + doInHibernate( + this::sessionFactory, session -> { + session.createNamedQuery( "Dog.findAll", Dog.class ).list(); + } + ); + + // No there should be a QueryResultsCache named REGION + final QueryResultsCache queryResultsCache = cache.getQueryResultsCacheStrictly( REGION ); + assertNotNull( queryResultsCache ); + assertEquals( REGION, queryResultsCache.getRegion().getName() ); + + // Now there is a DomainDataRegion and QueryResultsRegion named REGION. + // Make sure that the same DomainDataRegion is returned by cache.getRegion( REGION ). + assertSame( domainDataRegion, cache.getRegion( REGION ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-13586") + public void testEvictCaches() { + + final Statistics statistics = sessionFactory().getStatistics(); + statistics.clear(); + + assertEquals( 0, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 0, statistics.getSecondLevelCachePutCount() ); + assertEquals( 0, statistics.getSecondLevelCacheMissCount() ); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 0, statistics.getQueryCachePutCount() ); + assertEquals( 0, statistics.getQueryCacheMissCount() ); + + doInHibernate( + this::sessionFactory, session -> { + + Dog yogi = session.get( Dog.class, "Yogi" ); + assertEquals( 1, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 0, statistics.getSecondLevelCachePutCount() ); + assertEquals( 0, statistics.getSecondLevelCacheMissCount() ); + // put the collection in the cache + Hibernate.initialize( yogi.nickNames ); + assertEquals( 1, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 1, statistics.getSecondLevelCachePutCount() ); + assertEquals( 1, statistics.getSecondLevelCacheMissCount() ); + + session.createNamedQuery( "Dog.findAll", Dog.class ).list(); + + assertEquals( 0, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + session.clear(); + + session.createNamedQuery( "Dog.findAll", Dog.class ).list(); + assertEquals( 1, statistics.getQueryCacheHitCount() ); + assertEquals( 1, statistics.getQueryCachePutCount() ); + assertEquals( 1, statistics.getQueryCacheMissCount() ); + + session.clear(); + statistics.clear(); + + sessionFactory().getCache().evictRegion( REGION ); + + session.createNamedQuery( "Dog.findAll", Dog.class ).list(); + + assertEquals( 0, statistics.getSecondLevelCacheHitCount() ); + assertEquals( 0, statistics.getQueryCacheHitCount() ); + } + ); + + } + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true ); + ssrb.applySetting( AvailableSettings.USE_QUERY_CACHE, true ); + ssrb.applySetting( AvailableSettings.CACHE_REGION_PREFIX, PREFIX ); + ssrb.applySetting( AvailableSettings.CACHE_REGION_FACTORY, new CachingRegionFactory() ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void applyMetadataSources(MetadataSources metadataSources) { + super.applyMetadataSources( metadataSources ); + metadataSources.addAnnotatedClass( Dog.class ); + } + + @Before + public void setupData() { + doInHibernate( + this::sessionFactory, session -> { + Dog yogi = new Dog( "Yogi" ); + yogi.nickNames.add( "The Yog" ); + yogi.nickNames.add( "Little Boy" ); + yogi.nickNames.add( "Yogaroni Macaroni" ); + Dog irma = new Dog( "Irma" ); + irma.nickNames.add( "Squirmy" ); + irma.nickNames.add( "Bird" ); + session.persist( yogi ); + session.persist( irma ); + } + ); + } + + @After + public void cleanupData() { + doInHibernate( + this::sessionFactory, session -> { + List dogs = session.createQuery( "from Dog", Dog.class ).getResultList(); + for ( Dog dog : dogs ) { + session.delete( dog ); + } + } + ); + } + + @Entity(name = "Dog") + @NamedQuery(name = "Dog.findAll", query = QUERY, + hints = { + @QueryHint(name = "org.hibernate.cacheable", value = "true"), + @QueryHint(name = "org.hibernate.cacheRegion", value = REGION) + } + ) + @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region=REGION) + public static class Dog { + @Id + private String name; + + @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region=REGION) + @ElementCollection + private Set nickNames = new HashSet<>(); + + public Dog(String name) { + this.name = name; + } + + public Dog() { + } + } +} From 2076c68ddff5dc39055e90e162a34c99c72261cb Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Fri, 23 Aug 2019 22:40:36 -0700 Subject: [PATCH 85/99] HHH-13586 : ClassCastException when using a single region name for both entity and query results --- .../cache/internal/EnabledCaching.java | 40 +++++++++++++------ .../hibernate/cache/spi/CacheImplementor.java | 4 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java index e7257ed0d0..9ce34807a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java @@ -48,6 +48,7 @@ import org.hibernate.pretty.MessageHelper; /** * @author Steve Ebersole * @author Strong Liu + * @author Gail Badner */ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildingContext { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EnabledCaching.class ); @@ -57,6 +58,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin private final Map regionsByName = new ConcurrentHashMap<>(); + // A map by name for QueryResultsRegion instances that have the same name as a Region + // in #regionsByName. + private final Map queryResultsRegionsByDuplicateName = new ConcurrentHashMap<>(); + private final Map entityAccessMap = new ConcurrentHashMap<>(); private final Map naturalIdAccessMap = new ConcurrentHashMap<>(); private final Map collectionAccessMap = new ConcurrentHashMap<>(); @@ -204,6 +209,8 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin @Override public Region getRegion(String regionName) { + // The Region in regionsByName has precedence over the + // QueryResultsRegion in #queryResultsRegionsByDuplicateName return regionsByName.get( regionName ); } @@ -488,12 +495,23 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin } protected QueryResultsCache makeQueryResultsRegionAccess(String regionName) { - final QueryResultsRegion region = (QueryResultsRegion) regionsByName.computeIfAbsent( + final Region region = regionsByName.computeIfAbsent( regionName, this::makeQueryResultsRegion ); + final QueryResultsRegion queryResultsRegion; + if ( QueryResultsRegion.class.isInstance( region ) ) { + queryResultsRegion = (QueryResultsRegion) region; + } + else { + // There was already a different type of Region with the same name. + queryResultsRegion = queryResultsRegionsByDuplicateName.computeIfAbsent( + regionName, + this::makeQueryResultsRegion + ); + } final QueryResultsCacheImpl regionAccess = new QueryResultsCacheImpl( - region, + queryResultsRegion, timestampsCache ); namedQueryResultsCacheMap.put( regionName, regionAccess ); @@ -502,20 +520,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin } protected QueryResultsRegion makeQueryResultsRegion(String regionName) { - // make sure there is not an existing domain-data region with that name.. - final Region existing = regionsByName.get( regionName ); - if ( existing != null ) { - if ( !QueryResultsRegion.class.isInstance( existing ) ) { - throw new IllegalStateException( "Cannot store both domain-data and query-result-data in the same region [" + regionName ); - } - - throw new IllegalStateException( "Illegal call to create QueryResultsRegion - one already existed" ); - } - return regionFactory.buildQueryResultsRegion( regionName, getSessionFactory() ); } - @Override public Set getCacheRegionNames() { return regionsByName.keySet(); @@ -524,6 +531,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin @Override public void evictRegion(String regionName) { getRegion( regionName ).clear(); + final QueryResultsRegion queryResultsRegionWithDuplicateName = queryResultsRegionsByDuplicateName.get( regionName ); + if ( queryResultsRegionWithDuplicateName != null ) { + queryResultsRegionWithDuplicateName.clear(); + } } @Override @@ -545,6 +556,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin for ( Region region : regionsByName.values() ) { region.destroy(); } + for ( Region region : queryResultsRegionsByDuplicateName.values() ) { + region.destroy(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java index 00a28c7b99..c77b13db7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java @@ -52,7 +52,9 @@ public interface CacheImplementor extends Service, Cache, org.hibernate.engine.s void prime(Set cacheRegionConfigs); /** - * Get a cache Region by name + * Get a cache Region by name. If there is both a {@link DomainDataRegion} + * and a {@link QueryResultsRegion} with the specified name, then the + * {@link DomainDataRegion} will be returned. * * @apiNote It is only valid to call this method after {@link #prime} has * been performed From b26ec4e6255a9e17dda7dfbc299d1f37ad51d136 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 3 Sep 2019 14:43:37 -0700 Subject: [PATCH 86/99] HHH-13611 : Restore EntityMetamodel constructor to take SessionFactoryImplementor argument instead of PersisterCreationContext --- .../spi/interceptor/EnhancementHelper.java | 5 +-- .../interceptor/LazyAttributesMetadata.java | 7 ++-- .../entity/AbstractEntityPersister.java | 20 ++---------- .../org/hibernate/tuple/PropertyFactory.java | 7 ++-- .../BytecodeEnhancementMetadataPojoImpl.java | 6 ++-- .../tuple/entity/EntityMetamodel.java | 32 +++---------------- .../test/legacy/CustomPersister.java | 2 +- 7 files changed, 16 insertions(+), 63 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java index 91d865ac49..7c509cfe32 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java @@ -8,7 +8,6 @@ package org.hibernate.bytecode.enhance.spi.interceptor; import java.util.Locale; import java.util.function.BiFunction; -import java.util.function.Function; import org.hibernate.FlushMode; import org.hibernate.LazyInitializationException; @@ -31,8 +30,7 @@ public class EnhancementHelper { public static boolean includeInBaseFetchGroup( Property bootMapping, boolean isEnhanced, - boolean allowEnhancementAsProxy, - Function hasSubclassChecker) { + boolean allowEnhancementAsProxy) { final Value value = bootMapping.getValue(); if ( ! isEnhanced ) { @@ -57,7 +55,6 @@ public class EnhancementHelper { } // include it in the base fetch group so long as the config allows // using the FK to create an "enhancement proxy" -// return allowEnhancementAsProxy && hasSubclassChecker.apply( toOne.getReferencedEntityName() ); return allowEnhancementAsProxy; } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributesMetadata.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributesMetadata.java index 380b17711c..8192d3d132 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributesMetadata.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributesMetadata.java @@ -16,7 +16,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; @@ -34,8 +33,7 @@ public class LazyAttributesMetadata implements Serializable { public static LazyAttributesMetadata from( PersistentClass mappedEntity, boolean isEnhanced, - boolean allowEnhancementAsProxy, - Function hasSubclassChecker) { + boolean allowEnhancementAsProxy) { final Map lazyAttributeDescriptorMap = new LinkedHashMap<>(); final Map> fetchGroupToAttributesMap = new HashMap<>(); @@ -48,8 +46,7 @@ public class LazyAttributesMetadata implements Serializable { final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( property, isEnhanced, - allowEnhancementAsProxy, - hasSubclassChecker + allowEnhancementAsProxy ); if ( lazy ) { final LazyAttributeDescriptor lazyAttributeDescriptor = LazyAttributeDescriptor.from( property, i, x++ ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index d2b78ee95f..1a3265db1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -568,7 +568,7 @@ public abstract class AbstractEntityPersister this.naturalIdRegionAccessStrategy = null; } - this.entityMetamodel = new EntityMetamodel( persistentClass, this, creationContext ); + this.entityMetamodel = new EntityMetamodel( persistentClass, this, creationContext.getSessionFactory() ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); if ( entityMetamodel.isMutable() ) { @@ -704,14 +704,7 @@ public abstract class AbstractEntityPersister final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( prop, entityMetamodel.isInstrumented(), - creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled(), - associatedEntityName -> { - final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName ); - if ( bootEntityDescriptor == null ) { - return false; - } - return bootEntityDescriptor.hasSubclasses(); - } + creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled() ); if ( lazy ) { @@ -787,14 +780,7 @@ public abstract class AbstractEntityPersister final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( prop, entityMetamodel.isInstrumented(), - creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled(), - associatedEntityName -> { - final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName ); - if ( bootEntityDescriptor == null ) { - return false; - } - return bootEntityDescriptor.hasSubclasses(); - } + creationContext.getSessionFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled() ); while ( colIter.hasNext() ) { Selectable thing = (Selectable) colIter.next(); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index a8004fa92f..779c114e70 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -7,7 +7,6 @@ package org.hibernate.tuple; import java.lang.reflect.Constructor; -import java.util.function.Function; import org.hibernate.EntityMode; import org.hibernate.HibernateException; @@ -155,8 +154,7 @@ public final class PropertyFactory { SessionFactoryImplementor sessionFactory, int attributeNumber, Property property, - boolean lazyAvailable, - Function hasSubclassChecker) { + boolean lazyAvailable) { final Type type = property.getValue().getType(); final NonIdentifierAttributeNature nature = decode( type ); @@ -174,8 +172,7 @@ public final class PropertyFactory { final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( property, lazyAvailable, - sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(), - hasSubclassChecker + sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled() ); switch ( nature ) { diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java index 390db7b179..cad2b64c9f 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/BytecodeEnhancementMetadataPojoImpl.java @@ -8,7 +8,6 @@ package org.hibernate.tuple.entity; import java.io.Serializable; import java.util.Set; -import java.util.function.Function; import org.hibernate.LockMode; import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; @@ -38,12 +37,11 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc PersistentClass persistentClass, Set identifierAttributeNames, CompositeType nonAggregatedCidMapper, - boolean allowEnhancementAsProxy, - Function hasSubclassChecker) { + boolean allowEnhancementAsProxy) { final Class mappedClass = persistentClass.getMappedClass(); final boolean enhancedForLazyLoading = PersistentAttributeInterceptable.class.isAssignableFrom( mappedClass ); final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading - ? LazyAttributesMetadata.from( persistentClass, true, allowEnhancementAsProxy, hasSubclassChecker ) + ? LazyAttributesMetadata.from( persistentClass, true, allowEnhancementAsProxy ) : LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() ); return new BytecodeEnhancementMetadataPojoImpl( diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 0e4de4531e..298cd24482 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -33,7 +33,6 @@ import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.IdentifierProperty; import org.hibernate.tuple.InDatabaseValueGenerationStrategy; @@ -128,8 +127,8 @@ public class EntityMetamodel implements Serializable { public EntityMetamodel( PersistentClass persistentClass, EntityPersister persister, - final PersisterCreationContext creationContext) { - this.sessionFactory = creationContext.getSessionFactory(); + SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; name = persistentClass.getEntityName(); rootName = persistentClass.getRootClass().getEntityName(); @@ -164,14 +163,7 @@ public class EntityMetamodel implements Serializable { persistentClass, idAttributeNames, nonAggregatedCidMapper, - sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(), - associatedEntityName -> { - final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName ); - if ( bootEntityDescriptor == null ) { - return false; - } - return bootEntityDescriptor.hasSubclasses(); - } + sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled() ); } else { @@ -234,14 +226,7 @@ public class EntityMetamodel implements Serializable { sessionFactory, i, prop, - bytecodeEnhancementMetadata.isEnhancedForLazyLoading(), - associatedEntityName -> { - final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName ); - if ( bootEntityDescriptor == null ) { - return false; - } - return bootEntityDescriptor.hasSubclasses(); - } + bytecodeEnhancementMetadata.isEnhancedForLazyLoading() ); } @@ -260,14 +245,7 @@ public class EntityMetamodel implements Serializable { boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( prop, bytecodeEnhancementMetadata.isEnhancedForLazyLoading(), - sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled(), - associatedEntityName -> { - final PersistentClass bootEntityDescriptor = creationContext.getMetadata().getEntityBinding( associatedEntityName ); - if ( bootEntityDescriptor == null ) { - return false; - } - return bootEntityDescriptor.hasSubclasses(); - } + sessionFactory.getSessionFactoryOptions().isEnhancementAsProxyEnabled() ); if ( lazy ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index d79fa014e8..833cf33961 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -71,7 +71,7 @@ public class CustomPersister implements EntityPersister { NaturalIdDataAccess naturalIdRegionAccessStrategy, PersisterCreationContext creationContext) { this.factory = creationContext.getSessionFactory(); - this.entityMetamodel = new EntityMetamodel( model, this, creationContext ); + this.entityMetamodel = new EntityMetamodel( model, this, creationContext.getSessionFactory() ); } public boolean hasLazyProperties() { From ea8694ad38a3cc06972523e46e35317ad4f4f9b9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 9 Sep 2019 10:32:17 +0100 Subject: [PATCH 87/99] HHH-13611 : Restore EntityMetamodel constructor to take SessionFactoryImplementor argument instead of PersisterCreationContext --- .../org/hibernate/persister/entity/AbstractEntityPersister.java | 2 +- .../test/java/org/hibernate/test/legacy/CustomPersister.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 1a3265db1f..7fea63ab50 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -568,7 +568,7 @@ public abstract class AbstractEntityPersister this.naturalIdRegionAccessStrategy = null; } - this.entityMetamodel = new EntityMetamodel( persistentClass, this, creationContext.getSessionFactory() ); + this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); if ( entityMetamodel.isMutable() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 833cf33961..9615248f4f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -71,7 +71,7 @@ public class CustomPersister implements EntityPersister { NaturalIdDataAccess naturalIdRegionAccessStrategy, PersisterCreationContext creationContext) { this.factory = creationContext.getSessionFactory(); - this.entityMetamodel = new EntityMetamodel( model, this, creationContext.getSessionFactory() ); + this.entityMetamodel = new EntityMetamodel( model, this, factory ); } public boolean hasLazyProperties() { From f0218e82adaf96fb5b20017a6845d0fabecfb5dc Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 10 Sep 2019 15:51:11 +0100 Subject: [PATCH 88/99] HHH-13616 Enable the hibernate-orm-modules test for JDK 11 --- build.gradle | 2 +- gradle/libraries.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 56177 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- .../hibernate-orm-modules.gradle | 9 ++++----- .../HibernateEnversOnWildflyTest.java | 2 ++ .../HibernateModulesOnWildflyTest.java | 2 ++ .../TransactionRollbackTest.java | 1 + .../AuditedEntity.java | 2 +- .../Kryptonite.java | 2 +- 10 files changed, 16 insertions(+), 12 deletions(-) rename hibernate-orm-modules/src/test/java/org/hibernate/wildfly/{integrationtest => model}/AuditedEntity.java (96%) rename hibernate-orm-modules/src/test/java/org/hibernate/wildfly/{integrationtest => model}/Kryptonite.java (90%) diff --git a/build.gradle b/build.gradle index 2416a7aedf..7eb33121c9 100644 --- a/build.gradle +++ b/build.gradle @@ -86,7 +86,7 @@ task ciBuild { wrapper { - gradleVersion = '4.10.2' + gradleVersion = '4.10.3' distributionType = Wrapper.DistributionType.ALL } diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 4fbbdce617..98fa8af98c 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -28,11 +28,11 @@ ext { geolatteVersion = '1.4.0' // Wildfly version targeted by module ZIP; Arquillian/Shrinkwrap versions used for CDI testing and testing the module ZIP - wildflyVersion = '14.0.1.Final' - arquillianVersion = '1.4.0.Final' + wildflyVersion = '17.0.1.Final' + arquillianVersion = '1.4.1.Final' shrinkwrapVersion = '1.2.6' shrinkwrapDescriptorsVersion = '2.0.0' - wildflyArquillianContainerVersion = '2.1.1.Final' + wildflyArquillianContainerVersion = '2.2.0.Final' jodaTimeVersion = '2.3' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 28861d273a5d270fd8f65dd74570c17c9c507736..94336fcae912db8a11d55634156fa011f4686124 100644 GIT binary patch delta 692 zcmYk4T}V@59LD$ljeUf+K{mFtg;7@>Yq^4!Io(R7$e0sDrO|(Ka zAd0cFi^%R0#%_X^!lG@SxeqtD^>I-IyO3~nThQ_4w0z<4{+{1+&VfJgyA^L~#9P7+ zoxbn&+g?vz^P9nxt3WZr!<$}hFc56crP8mWM+UC)f}^G13t>lf<}otyw#W>u8H(R zrzkB>XN?H12~;&TmELSbw`4$F)+saBX6X`@56Ni3ywt}cNJ!)K*~Yu$*>3u9g5w<- z?>RV1SqC+SICfla<^Nmq7+U4Aloj>xGP~Y}ipPY|x=1g*is~iR1V`J%LAY`v*(Q0M z&kfLCpMn~=eEYDS6IlS3yqA52KkuZrX)OEAa7_#Nk{9WTUyi*p8ZcH6*fq=)rdR{H z!WfO*LcU-{y=sN8DA0>Jj8u-`dC|^7$QN&tBf!PfhLeun<(SwQq|kkiM@z%hALJMa zTJf-KK&m8AJj|80JV-Hc+{R8TQsS`^)%adMOQomHcyY6bijij2SJa4Yc2V)!F6CxP zcq`YL6>lnjy82444#QpTV*Qw@j?%&^qN_G|iw5ke3AB{NO47-P_SXb<8xLwX*f4(9 XPFi-~FVnxH;QJzaK6R>pr*^_$Jgg(_ delta 717 zcmYk4Ur19?9LMkZS?G# zyeoQy%FYN}jB3ywJwTeXYg)_NVUD?31}(7~mW!vcCc4**XE7rp%_VTfRXWvzWv>Z{ zAEc#Lfi>}+*p^VJ{)*6}i7GaLn~4VMYU6~V735g433OwW4+ZcRC97#vG+J>{U6_XU1QRa4x{u^LSuRf;|o zsi7|q. */ -package org.hibernate.wildfly.integrationtest; +package org.hibernate.wildfly.model; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/Kryptonite.java b/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/model/Kryptonite.java similarity index 90% rename from hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/Kryptonite.java rename to hibernate-orm-modules/src/test/java/org/hibernate/wildfly/model/Kryptonite.java index 7b073fe6f3..3ab61e224b 100644 --- a/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/integrationtest/Kryptonite.java +++ b/hibernate-orm-modules/src/test/java/org/hibernate/wildfly/model/Kryptonite.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.wildfly.integrationtest; +package org.hibernate.wildfly.model; import javax.persistence.Entity; import javax.persistence.Id; From be7cc76556f1a2efd715b24ba4516fba0ddbbec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 26 Aug 2019 13:53:29 +0200 Subject: [PATCH 89/99] HHH-13582 Upgrade MySQL Connector/J to 8.0.17 ConnectorJ 8 is the version used in WildFly integration tests. ConnectorJ 5 is apparently no longer tested. Note this solves most timezone-related issues we've been having. --- gradle/libraries.gradle | 3 +-- .../hibernate/test/annotations/EntityTest.java | 6 ++---- .../autocommit/MySQLSkipAutoCommitTest.java | 17 +++++++++++++---- .../test/temporal/TimePropertyTest.java | 7 ++++++- .../databases/mysql56/matrix.gradle | 2 +- .../databases/mysql8/matrix.gradle | 2 +- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 98fa8af98c..16d24e151b 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -109,8 +109,7 @@ ext { hsqldb: "org.hsqldb:hsqldb:2.3.2", derby: "org.apache.derby:derby:10.11.1.1", postgresql: 'org.postgresql:postgresql:42.2.2', - //Upgrade MySQL Driver only when this issue gets fixed: https://bugs.mysql.com/bug.php?id=85941 - mysql: 'mysql:mysql-connector-java:5.1.46', + mysql: 'mysql:mysql-connector-java:8.0.17', mariadb: 'org.mariadb.jdbc:mariadb-java-client:2.2.3', oracle: 'com.oracle.jdbc:ojdbc8:12.2.0.1', diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java index afaf60ebd8..5dd684f870 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java @@ -24,9 +24,7 @@ import org.hibernate.Transaction; import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.MySQL57Dialect; -import org.hibernate.dialect.MySQL8Dialect; -import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.Oracle10gDialect; import org.hibernate.query.Query; import org.hibernate.tool.hbm2ddl.SchemaExport; @@ -373,7 +371,7 @@ public class EntityTest extends BaseNonConfigCoreFunctionalTestCase { @SkipForDialect(value = Oracle10gDialect.class, comment = "oracle12c returns time in getDate. For now, skip.") public void testTemporalType() throws Exception { - final ZoneId zoneId = ( Dialect.getDialect() instanceof MySQL8Dialect ) ? ZoneId.of( "UTC") + final ZoneId zoneId = ( Dialect.getDialect() instanceof MySQL5Dialect ) ? ZoneId.of( "UTC") : ZoneId.systemDefault(); Flight airFrance = doInHibernate( this::sessionFactory, session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/autocommit/MySQLSkipAutoCommitTest.java b/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/autocommit/MySQLSkipAutoCommitTest.java index b8f61a87f7..a417dc99fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/autocommit/MySQLSkipAutoCommitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/autocommit/MySQLSkipAutoCommitTest.java @@ -30,11 +30,20 @@ public class MySQLSkipAutoCommitTest extends AbstractSkipAutoCommitTest { if ( getDialect() instanceof MariaDBDialect ) { dataSource = ReflectionUtil.newInstance( "org.mariadb.jdbc.MariaDbDataSource" ); } - else if ( getDialect() instanceof MySQL8Dialect ) { - dataSource = ReflectionUtil.newInstance( "com.mysql.cj.jdbc.MysqlDataSource" ); - } else if ( getDialect() instanceof MySQLDialect ) { - dataSource = ReflectionUtil.newInstance( "com.mysql.jdbc.jdbc2.optional.MysqlDataSource" ); + try { + // ConnectorJ 8 + dataSource = ReflectionUtil.newInstance( "com.mysql.cj.jdbc.MysqlDataSource" ); + } + catch (IllegalArgumentException e) { + try { + // ConnectorJ 5 + dataSource = ReflectionUtil.newInstance( "com.mysql.jdbc.jdbc2.optional.MysqlDataSource" ); + } catch (Exception e2) { + e2.addSuppressed( e ); + throw e; + } + } } ReflectionUtil.setProperty( dataSource, "url", Environment.getProperties().getProperty( AvailableSettings.URL ) ); ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/temporal/TimePropertyTest.java b/hibernate-core/src/test/java/org/hibernate/test/temporal/TimePropertyTest.java index 935d011c14..d77d122861 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/temporal/TimePropertyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/temporal/TimePropertyTest.java @@ -9,6 +9,7 @@ package org.hibernate.test.temporal; import java.sql.Time; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import javax.persistence.GeneratedValue; import javax.persistence.Id; @@ -36,7 +37,11 @@ public class TimePropertyTest extends BaseCoreFunctionalTestCase { @Test public void testTimeAsDate() { final Entity eOrig = new Entity(); - eOrig.tAsDate = new Time( new Date().getTime() ); + Calendar calendar = Calendar.getInstance(); + // See javadoc for java.sql.Time: 'The date components should be set to the "zero epoch" value of January 1, 1970 and should not be accessed' + // Other dates can potentially lead to errors in JDBC drivers, in particular MySQL ConnectorJ 8.x. + calendar.set( 1970, Calendar.JANUARY, 1 ); + eOrig.tAsDate = new Time( calendar.getTimeInMillis() ); Session s = openSession(); s.getTransaction().begin(); diff --git a/hibernate-spatial/databases/mysql56/matrix.gradle b/hibernate-spatial/databases/mysql56/matrix.gradle index 68f6ea4f81..361a524b53 100644 --- a/hibernate-spatial/databases/mysql56/matrix.gradle +++ b/hibernate-spatial/databases/mysql56/matrix.gradle @@ -4,4 +4,4 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -jdbcDependency "mysql:mysql-connector-java:5.1.15" +jdbcDependency "mysql:mysql-connector-java:8.0.17" diff --git a/hibernate-spatial/databases/mysql8/matrix.gradle b/hibernate-spatial/databases/mysql8/matrix.gradle index 8ddca02827..361a524b53 100644 --- a/hibernate-spatial/databases/mysql8/matrix.gradle +++ b/hibernate-spatial/databases/mysql8/matrix.gradle @@ -4,4 +4,4 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -jdbcDependency "mysql:mysql-connector-java:8.0.12" +jdbcDependency "mysql:mysql-connector-java:8.0.17" From a580227569fd4ec362b5d1b387801eabd8b24f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 27 Aug 2019 11:47:07 +0200 Subject: [PATCH 90/99] HHH-13582 Ignore LocalDateTest for MySQL MySQL ConnectorJ 8.x returns the wrong date when the JVM default timezone is different from the server timezone: https://bugs.mysql.com/bug.php?id=91112 --- .../test/java/org/hibernate/test/type/LocalDateTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java index 50a00f62c2..5f370c8b02 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java @@ -23,6 +23,7 @@ import javax.persistence.Id; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.MariaDBDialect; +import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor; @@ -34,7 +35,13 @@ import org.junit.runners.Parameterized; * Tests for storage of LocalDate properties. */ @TestForIssue(jiraKey = "HHH-10371") -@SkipForDialect(value = AbstractHANADialect.class, comment = "HANA systematically returns the wrong date when the JVM default timezone is not UTC") +@SkipForDialect(value = AbstractHANADialect.class, + comment = "HANA systematically returns the wrong date when the JVM default timezone is not UTC") +@SkipForDialect(value = MySQL5Dialect.class, + comment = "HHH-13582: MySQL ConnectorJ 8.x returns the wrong date" + + " when the JVM default timezone is different from the server timezone:" + + " https://bugs.mysql.com/bug.php?id=91112" +) public class LocalDateTest extends AbstractJavaTimeTypeTest { private static class ParametersBuilder extends AbstractParametersBuilder { From f9f6793232d5f5144f63622bf1ee1710b42ee74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 26 Aug 2019 18:11:57 +0200 Subject: [PATCH 91/99] HHH-13580 Fix a copy/paste error in OffsetTimeTest --- .../src/test/java/org/hibernate/test/type/OffsetTimeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java index 1d12f7f9ff..359742e57b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java @@ -34,7 +34,7 @@ import org.junit.Test; import org.junit.runners.Parameterized; /** - * Tests for storage of LocalTime properties. + * Tests for storage of OffsetTime properties. */ public class OffsetTimeTest extends AbstractJavaTimeTypeTest { From 8fce51c765311302305e52669ca778272e650144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 26 Aug 2019 11:44:53 +0200 Subject: [PATCH 92/99] HHH-13580 Use safer ZoneId => TimeZone conversion in AbstractJavaTimeTypeTest We were testing GMT instead of UTC-8 without even knowing it... --- .../test/type/AbstractJavaTimeTypeTest.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java index 69384e1675..8baef27657 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java @@ -191,7 +191,7 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase protected final void withDefaultTimeZone(Runnable runnable) { TimeZone timeZoneBefore = TimeZone.getDefault(); - TimeZone.setDefault( TimeZone.getTimeZone( env.defaultJvmTimeZone ) ); + TimeZone.setDefault( toTimeZone( env.defaultJvmTimeZone ) ); /* * Run the code in a new thread, because some libraries (looking at you, h2 JDBC driver) * cache data dependent on the default timezone in thread local variables, @@ -223,6 +223,23 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase } } + private static TimeZone toTimeZone(ZoneId zoneId) { + String idString = zoneId.getId(); + if ( idString.startsWith( "UTC+" ) || idString.startsWith( "UTC-" ) ) { + // Apparently TimeZone doesn't understand UTC+XXX nor UTC-XXX + // Using GMT+XXX or GMT-XXX as a fallback + idString = "GMT" + idString.substring( "UTC".length() ); + } + + TimeZone result = TimeZone.getTimeZone( idString ); + if ( !idString.equals( result.getID() ) ) { + // If the timezone is not understood, getTimeZone returns GMT and the condition above is true + throw new IllegalStateException( "Attempting to test an unsupported timezone: " + zoneId ); + } + + return result; + } + protected final Class getRemappingDialectClass() { return env.remappingDialectClass; } From 74d35b2d59c4fff09f40df6d393800c88dfdda7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 26 Aug 2019 13:31:17 +0200 Subject: [PATCH 93/99] HHH-13580 Ignore LocalTimeTest and OffsetTimeTest's 'nativeWriteThenRead' test for MySQL The returned time is right (otherwise #writeThenRead would fail), it's just that a different day is returned, but it won't affect the LocalTime representation manipulated by the user. --- .../test/java/org/hibernate/test/type/LocalTimeTest.java | 7 +++++++ .../test/java/org/hibernate/test/type/OffsetTimeTest.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java index e4139ed48c..36bceb0235 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java @@ -23,6 +23,7 @@ import javax.persistence.Id; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.MariaDBDialect; +import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor; @@ -186,6 +187,12 @@ public class LocalTimeTest extends AbstractJavaTimeTypeTest Date: Mon, 26 Aug 2019 16:29:34 +0200 Subject: [PATCH 94/99] HHH-13581 Disable tests involving MariaDB ConnectorJ's buggy ResultSet#getTime(int, Calendar) method --- .../java/org/hibernate/test/type/LocalTimeTest.java | 13 +++++++++++++ .../org/hibernate/test/type/OffsetTimeTest.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java index 36bceb0235..73a5ad6b70 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java @@ -15,6 +15,7 @@ import java.sql.Types; import java.time.LocalTime; import java.time.ZoneId; import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.persistence.Basic; import javax.persistence.Column; @@ -56,6 +57,18 @@ public class LocalTimeTest extends AbstractJavaTimeTypeTest getHibernateJdbcTimeZonesToTest() { + // The MariaDB Connector/J JDBC driver has a bug in ResultSet#getTime(int, Calendar) + // that prevents our explicit JDBC timezones from being recognized + // See https://hibernate.atlassian.net/browse/HHH-13581 + // See https://jira.mariadb.org/browse/CONJ-724 + if ( MariaDBDialect.class.isInstance( getDialect() ) ) { + return Collections.emptySet(); + } + return super.getHibernateJdbcTimeZonesToTest(); + } } @Parameterized.Parameters(name = "{1}:{2}:{3}.{4} (JDBC write date: {5}-{6}-{7}) {0}") diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java index cb83f66941..16bef9c237 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java @@ -17,6 +17,7 @@ import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.persistence.Basic; import javax.persistence.Column; @@ -59,6 +60,18 @@ public class OffsetTimeTest extends AbstractJavaTimeTypeTest getHibernateJdbcTimeZonesToTest() { + // The MariaDB Connector/J JDBC driver has a bug in ResultSet#getTime(int, Calendar) + // that prevents our explicit JDBC timezones from being recognized + // See https://hibernate.atlassian.net/browse/HHH-13581 + // See https://jira.mariadb.org/browse/CONJ-724 + if ( MariaDBDialect.class.isInstance( getDialect() ) ) { + return Collections.emptySet(); + } + return super.getHibernateJdbcTimeZonesToTest(); + } } @Parameterized.Parameters(name = "{1}:{2}:{3}.{4}[{5}] (JDBC write date: {6}-{7}-{8}) {0}") From ef87991fa39478234bcbc5b274eafbf4aadcbc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 30 Aug 2019 14:42:01 +0200 Subject: [PATCH 95/99] HHH-13605 Upgrade the MariaDB JDBC driver to 2.2.4 in the matrix_mariadb task It's already the version we use when running test -Pdb=mariadb, so let's be consistent. --- databases/mariadb/matrix.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/databases/mariadb/matrix.gradle b/databases/mariadb/matrix.gradle index 7ebdee1458..39b03e208f 100644 --- a/databases/mariadb/matrix.gradle +++ b/databases/mariadb/matrix.gradle @@ -4,4 +4,4 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -jdbcDependency 'org.mariadb.jdbc:mariadb-java-client:1.5.7' \ No newline at end of file +jdbcDependency 'org.mariadb.jdbc:mariadb-java-client:2.2.4' \ No newline at end of file From 26c2e0bf2b4d714cad340f3ebb3b18a498095168 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 13 Sep 2019 11:09:25 +0100 Subject: [PATCH 96/99] HHH-13621 Add test for issue --- ...ctionPropertyValueEndingWithSpaceTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java b/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java new file mode 100644 index 0000000000..70b35aaacb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java @@ -0,0 +1,75 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.tool.schema; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; +import org.hibernate.cfg.Configuration; +import org.hibernate.internal.util.config.ConfigurationHelper; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +/** + * @author Andrea Boriero + */ +@TestForIssue(jiraKey = "HHH-13621") +public class SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest extends BaseCoreFunctionalTestCase { + + private File dropOutput; + private File createOutput; + + @Override + protected StandardServiceRegistryImpl buildServiceRegistry( + BootstrapServiceRegistry bootRegistry, + Configuration configuration) { + try { + dropOutput = File.createTempFile( "drop_script", ".sql" ); + createOutput = File.createTempFile( "create_script", ".sql" ); + dropOutput.deleteOnExit(); + createOutput.deleteOnExit(); + } + catch (IOException e) { + fail( "unable to create temp file" + e ); + } + Properties properties = new Properties(); + properties.putAll( configuration.getProperties() ); + // the value of the property ends with a space + properties.setProperty( "javax.persistence.schema-generation.scripts.action", "drop-and-create " ); + properties.setProperty( + "javax.persistence.schema-generation.scripts.create-target", + createOutput.getAbsolutePath() + ); + properties.setProperty( + "javax.persistence.schema-generation.scripts.drop-target", + dropOutput.getAbsolutePath() + ); + ConfigurationHelper.resolvePlaceHolders( properties ); + + StandardServiceRegistryBuilder cfgRegistryBuilder = configuration.getStandardServiceRegistryBuilder(); + + StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder( + bootRegistry, + cfgRegistryBuilder.getAggregatedCfgXml() + ).applySettings( properties ); + + prepareBasicRegistryBuilder( registryBuilder ); + return (StandardServiceRegistryImpl) registryBuilder.build(); + } + + @Test + public void testValueEndingWithSpaceDoesNotCauseExceptionDuringBootstrap() { + } +} From f5877052ff00cf923628d0c1859cc1f7779df500 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 13 Sep 2019 11:24:11 +0100 Subject: [PATCH 97/99] HHH-13621 Exception if spaces after value of javax.persistence.schema-generation.scripts.action in hibernate.properties --- .../src/main/java/org/hibernate/tool/schema/Action.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java index c7c5b375f5..8265628b32 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java @@ -101,7 +101,7 @@ public enum Action { return (Action) value; } - final String name = value.toString(); + final String name = value.toString().trim(); if ( name.isEmpty() || NONE.externalJpaName.equals( name ) ) { // default is NONE return NONE; From 57dbfbb60e1a97708a0a4f004fc3f4101d86ef97 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 16 Sep 2019 16:22:56 +0100 Subject: [PATCH 98/99] HHH-13622 Upgrade the WildFly Transaction Client to 1.1.7.Final --- gradle/libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 16d24e151b..2b0a94227e 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -158,7 +158,7 @@ ext { wildfly_arquillian_container_managed: "org.wildfly.arquillian:wildfly-arquillian-container-managed:${wildflyArquillianContainerVersion}", jboss_vfs: "org.jboss:jboss-vfs:3.2.11.Final", jipijapa_spi: "org.wildfly:jipijapa-spi:${wildflyVersion}", - wildfly_transaction_client : 'org.wildfly.transaction:wildfly-transaction-client:1.0.3.Final', + wildfly_transaction_client : 'org.wildfly.transaction:wildfly-transaction-client:1.1.7.Final', jboss_ejb_spec_jar : 'org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Final', jboss_annotation_spec_jar : 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final' From 0f2a037e1880a074b6328ea3f2bcd86a161469cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 30 Aug 2019 14:57:19 +0200 Subject: [PATCH 99/99] HHH-13606 Upgrade the HANA JDBC driver to 2.4.59 in the matrix_hana task It's already the version we use when running test -Pdb=hana, so let's be consistent. --- databases/hana/matrix.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/databases/hana/matrix.gradle b/databases/hana/matrix.gradle index 8782b540d7..59d7bc269e 100644 --- a/databases/hana/matrix.gradle +++ b/databases/hana/matrix.gradle @@ -5,4 +5,4 @@ * See the lgpl.txt file in the root directory or . */ -jdbcDependency 'com.sap.cloud.db.jdbc:ngdbc:2.2.16' \ No newline at end of file +jdbcDependency 'com.sap.cloud.db.jdbc:ngdbc:2.4.59' \ No newline at end of file