diff --git a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java index a064ede186bc..703b5b9be449 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java @@ -8,6 +8,7 @@ import java.util.TimeZone; import java.util.function.UnaryOperator; +import org.hibernate.engine.creation.CommonBuilder; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -18,7 +19,7 @@ * * @see SessionFactory#withOptions() */ -public interface SessionBuilder { +public interface SessionBuilder extends CommonBuilder { /** * Opens a session with the specified options. * @@ -26,41 +27,13 @@ public interface SessionBuilder { */ Session openSession(); - /** - * Adds a specific interceptor to the session options. - * - * @param interceptor The interceptor to use. - * - * @return {@code this}, for method chaining - */ + @Override SessionBuilder interceptor(Interceptor interceptor); - /** - * Signifies that no {@link Interceptor} should be used. - *

- * By default, if no {@code Interceptor} is explicitly specified, the - * {@code Interceptor} associated with the {@link SessionFactory} is - * inherited by the new {@link Session}. - *

- * Calling {@link #interceptor(Interceptor)} with null has the same effect. - * - * @return {@code this}, for method chaining - */ + @Override SessionBuilder noInterceptor(); - /** - * Applies the given statement inspection function to the session. - * - * @param operator An operator which accepts a SQL string, returning - * a processed SQL string to be used by Hibernate - * instead of the given original SQL. Alternatively. - * the operator may work by side effect, and simply - * return the original SQL. - * - * @return {@code this}, for method chaining - * - * @since 7.0 - */ + @Override SessionBuilder statementInspector(UnaryOperator operator); /** @@ -163,60 +136,13 @@ public interface SessionBuilder { * @return {@code this}, for method chaining * @since 6.4 */ + @Override SessionBuilder tenantIdentifier(Object tenantIdentifier); - /** - * Specify a {@linkplain Session#isDefaultReadOnly read-only mode} - * for the session. If a session is created in read-only mode, then - * {@link Connection#setReadOnly} is called when a JDBC connection - * is obtained. - *

- * Furthermore, if read/write replication is in use, then: - *

- *

- * When read/write replication is in use, it's strongly recommended - * that the session be created with the {@linkplain #initialCacheMode - * initial cache mode} set to {@link CacheMode#GET}, to avoid writing - * stale data read from a read-only replica to the second-level cache. - * Hibernate cannot possibly guarantee that data read from a read-only - * replica is up to date. - *

- * When read/write replication is in use, it's possible that an item - * read from the second-level cache might refer to data which does not - * yet exist in the read-only replica. In this situation, an exception - * occurs when the association is fetched. To completely avoid this - * possibility, the {@linkplain #initialCacheMode initial cache mode} - * must be set to {@link CacheMode#IGNORE}. However, it's also usually - * possible to structure data access code in a way which eliminates - * this possibility. - *

- * If a session is created in read-only mode, then it cannot be - * changed to read-write mode, and any call to - * {@link Session#setDefaultReadOnly(boolean)} with fail. On the - * other hand, if a session is created in read-write mode, then it - * may later be switched to read-only mode, but all database access - * is directed to the writable replica. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#getReadOnlyConnection(Object) - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#releaseReadOnlyConnection(Object, Connection) - */ - @Incubating + @Override SessionBuilder readOnly(boolean readOnly); - /** - * Specify the initial {@link CacheMode} for the session. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see SharedSessionContract#getCacheMode() - */ + @Override SessionBuilder initialCacheMode(CacheMode cacheMode); /** diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionBuilder.java index ad4374c1363e..7983ae2f6fff 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionBuilder.java @@ -4,6 +4,7 @@ */ package org.hibernate; +import org.hibernate.engine.creation.CommonSharedBuilder; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -18,20 +19,12 @@ * * @see Session#sessionWithOptions() */ -public interface SharedSessionBuilder extends SessionBuilder { +public interface SharedSessionBuilder extends SessionBuilder, CommonSharedBuilder { - /** - * Signifies that the connection from the original session should be used to create the new session. - * - * @return {@code this}, for method chaining - */ + @Override SharedSessionBuilder connection(); - /** - * Signifies the interceptor from the original session should be used to create the new session. - * - * @return {@code this}, for method chaining - */ + @Override SharedSessionBuilder interceptor(); /** @@ -76,7 +69,7 @@ public interface SharedSessionBuilder extends SessionBuilder { SharedSessionBuilder statementInspector(StatementInspector statementInspector); @Override - SessionBuilder statementInspector(UnaryOperator operator); + SharedSessionBuilder statementInspector(UnaryOperator operator); @Override @Deprecated SharedSessionBuilder connectionHandlingMode(PhysicalConnectionHandlingMode mode); diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index bf1ced5dff72..36c692e338ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -26,6 +26,18 @@ * @author Steve Ebersole */ public interface SharedSessionContract extends QueryProducer, AutoCloseable, Serializable { + + /** + * Obtain a {@link StatelessSession} builder with the ability to copy certain + * information from this session. + * + * @return the session builder + * + * @since 7.2 + */ + @Incubating + SharedStatelessSessionBuilder statelessWithOptions(); + /** * Obtain the tenant identifier associated with this session, as a string. * diff --git a/hibernate-core/src/main/java/org/hibernate/SharedStatelessSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/SharedStatelessSessionBuilder.java new file mode 100644 index 000000000000..8243586d1341 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/SharedStatelessSessionBuilder.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import org.hibernate.engine.creation.CommonSharedBuilder; + +import java.util.function.UnaryOperator; + +/** + * Allows for creation of a {@linkplain StatelessSession stateless session} sharing the + * underpinnings of another {@linkplain Session stateful} or {@linkplain StatelessSession stateless} + * session. + * + * @see Session#statelessWithOptions() + * @see StatelessSession#statelessWithOptions() + * + * @since 7.2 + * + * @author Steve Ebersole + */ +@Incubating +public interface SharedStatelessSessionBuilder extends CommonSharedBuilder { + /** + * Open the stateless session. + */ + StatelessSession open(); + + @Override + SharedStatelessSessionBuilder connection(); + + @Override + SharedStatelessSessionBuilder interceptor(); + + @Override + SharedStatelessSessionBuilder interceptor(Interceptor interceptor); + + @Override + SharedStatelessSessionBuilder noInterceptor(); + + SharedStatelessSessionBuilder statementInspector(UnaryOperator operator); + + @Override + SharedStatelessSessionBuilder statementInspector(); + + @Override + SharedStatelessSessionBuilder noStatementInspector(); + + @Override + SharedStatelessSessionBuilder tenantIdentifier(Object tenantIdentifier); + + @Override + SharedStatelessSessionBuilder readOnly(boolean readOnly); + + @Override + SharedStatelessSessionBuilder initialCacheMode(CacheMode cacheMode); +} diff --git a/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java index 37d10c9d7e53..6f8658497490 100644 --- a/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/StatelessSessionBuilder.java @@ -7,6 +7,7 @@ import java.sql.Connection; import java.util.function.UnaryOperator; +import org.hibernate.engine.creation.CommonBuilder; import org.hibernate.resource.jdbc.spi.StatementInspector; /** @@ -16,7 +17,7 @@ * * @see SessionFactory#withStatelessOptions() */ -public interface StatelessSessionBuilder { +public interface StatelessSessionBuilder extends CommonBuilder { /** * Opens a session with the specified options. * @@ -24,110 +25,34 @@ public interface StatelessSessionBuilder { */ StatelessSession openStatelessSession(); - /** - * Adds a specific connection to the session options. - * - * @param connection The connection to use. - * - * @return {@code this}, for method chaining - */ + @Override StatelessSessionBuilder connection(Connection connection); - /** - * Specifies the connection handling modes for the session. - *

- * Note that if {@link ConnectionAcquisitionMode#IMMEDIATELY} is specified, - * then the release mode must be {@link ConnectionReleaseMode#ON_CLOSE}. - * - * @return {@code this}, for method chaining - * - * @since 7.2 - */ + @Override StatelessSessionBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); - /** - * Define the tenant identifier to be associated with the opened session. - * - * @param tenantIdentifier The tenant identifier. - * - * @return {@code this}, for method chaining - * @deprecated Use {@link #tenantIdentifier(Object)} instead - */ - @Deprecated(since = "6.4", forRemoval = true) - StatelessSessionBuilder tenantIdentifier(String tenantIdentifier); - - /** - * Define the tenant identifier to be associated with the opened session. - * - * @param tenantIdentifier The tenant identifier. - * - * @return {@code this}, for method chaining - * @since 6.4 - */ + @Override StatelessSessionBuilder tenantIdentifier(Object tenantIdentifier); - /** - * Specify a read-only mode for the stateless session. If a session - * is created in read-only mode, then {@link Connection#setReadOnly} - * is called when a JDBC connection is obtained. - *

- * Furthermore, if read/write replication is in use, then: - *

- *

- * When read/write replication is in use, it's strongly recommended - * that the session be created with the {@linkplain #initialCacheMode - * initial cache mode} set to {@link CacheMode#GET}, to avoid writing - * stale data read from a read-only replica to the second-level cache. - * Hibernate cannot possibly guarantee that data read from a read-only - * replica is up to date. It's also possible for a read-only session to - *

- * When read/write replication is in use, it's possible that an item - * read from the second-level cache might refer to data which does not - * yet exist in the read-only replica. In this situation, an exception - * occurs when the association is fetched. To completely avoid this - * possibility, the {@linkplain #initialCacheMode initial cache mode} - * must be set to {@link CacheMode#IGNORE}. However, it's also usually - * possible to structure data access code in a way which eliminates - * this possibility. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#getReadOnlyConnection(Object) - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#releaseReadOnlyConnection(Object, Connection) - */ - @Incubating + @Incubating @Override StatelessSessionBuilder readOnly(boolean readOnly); - /** - * Specify the initial {@link CacheMode} for the session. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see SharedSessionContract#getCacheMode() - */ + @Incubating @Override StatelessSessionBuilder initialCacheMode(CacheMode cacheMode); + @Override + StatelessSessionBuilder statementInspector(UnaryOperator operator); + /** - * Applies the given statement inspection function to the session. + * Define the tenant identifier to be associated with the opened session. * - * @param operator An operator which accepts a SQL string, returning - * a processed SQL string to be used by Hibernate - * instead of the given original SQL. Alternatively, - * the operator may work by side effect and simply - * return the original SQL. + * @param tenantIdentifier The tenant identifier. * * @return {@code this}, for method chaining - * - * @apiNote This operation exposes the SPI type - * {@link StatementInspector} - * and is therefore a layer-breaker. + * @deprecated Use {@link #tenantIdentifier(Object)} instead */ - StatelessSessionBuilder statementInspector(UnaryOperator operator); + @Deprecated(since = "6.4", forRemoval = true) + StatelessSessionBuilder tenantIdentifier(String tenantIdentifier); /** * Applies the given {@link StatementInspector} to the session. diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIncrementVersionProcess.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIncrementVersionProcess.java index 6ca5276cff37..4857eb6fcf80 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIncrementVersionProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIncrementVersionProcess.java @@ -5,7 +5,7 @@ package org.hibernate.action.internal; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.OptimisticLockHelper; /** @@ -33,9 +33,9 @@ public EntityIncrementVersionProcess(Object object) { * @param session The session on which the transaction is preparing to complete. */ @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { final var entry = session.getPersistenceContext().getEntry( object ); - // Don't increment version for an entity that is not in the PersistenceContext; + // Don't increment the version for an entity that is not in the PersistenceContext; if ( entry != null ) { OptimisticLockHelper.forceVersionIncrement( object, entry, session.asEventSource() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityVerifyVersionProcess.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityVerifyVersionProcess.java index 0483e8d7c02f..04986cf18bba 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityVerifyVersionProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityVerifyVersionProcess.java @@ -6,7 +6,7 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.dialect.lock.OptimisticEntityLockException; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.pretty.MessageHelper; /** @@ -29,9 +29,9 @@ public EntityVerifyVersionProcess(Object object) { } @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { final var entry = session.getPersistenceContext().getEntry( object ); - // Don't check version for an entity that is not in the PersistenceContext + // Don't check the version for an entity that is not in the PersistenceContext if ( entry != null ) { final Object latestVersion = entry.getPersister().getCurrentVersion( entry.getId(), session ); if ( !entry.getVersion().equals( latestVersion ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/action/spi/AfterTransactionCompletionProcess.java b/hibernate-core/src/main/java/org/hibernate/action/spi/AfterTransactionCompletionProcess.java index b353908dd0de..2a763a7498bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/spi/AfterTransactionCompletionProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/action/spi/AfterTransactionCompletionProcess.java @@ -4,19 +4,12 @@ */ package org.hibernate.action.spi; -import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks; /** * Contract representing some process that needs to occur during after transaction completion. * * @author Steve Ebersole */ -public interface AfterTransactionCompletionProcess { - /** - * Perform whatever processing is encapsulated here after completion of the transaction. - * - * @param success Did the transaction complete successfully? True means it did. - * @param session The session on which the transaction is completing. - */ - void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session); +public interface AfterTransactionCompletionProcess extends TransactionCompletionCallbacks.AfterCompletionCallback { } diff --git a/hibernate-core/src/main/java/org/hibernate/action/spi/BeforeTransactionCompletionProcess.java b/hibernate-core/src/main/java/org/hibernate/action/spi/BeforeTransactionCompletionProcess.java index 7ab190edf434..58949aa06fbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/spi/BeforeTransactionCompletionProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/action/spi/BeforeTransactionCompletionProcess.java @@ -5,17 +5,23 @@ package org.hibernate.action.spi; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks; /** * Contract representing some process that needs to occur during before transaction completion. * * @author Steve Ebersole */ -public interface BeforeTransactionCompletionProcess { +public interface BeforeTransactionCompletionProcess extends TransactionCompletionCallbacks.BeforeCompletionCallback { /** * Perform whatever processing is encapsulated here before completion of the transaction. * * @param session The session on which the transaction is preparing to complete. + * @deprecated Use {@linkplain #doBeforeTransactionCompletion(SharedSessionContractImplementor)} instead. */ - void doBeforeTransactionCompletion(SessionImplementor session); + @Deprecated(since = "7.2", forRemoval = true) + default void doBeforeTransactionCompletion(SessionImplementor session) { + doBeforeTransactionCompletion( (SharedSessionContractImplementor) session ); + } } 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 b08dba5fc91f..5cc3988a7d78 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 @@ -4,7 +4,6 @@ */ package org.hibernate.cache.internal; - import org.hibernate.HibernateException; import org.hibernate.action.internal.CollectionAction; import org.hibernate.boot.Metadata; @@ -23,7 +22,6 @@ import org.hibernate.integrator.spi.Integrator; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; - import org.jboss.logging.Logger; import static org.hibernate.cache.spi.SecondLevelCacheLogger.L2CACHE_LOGGER; @@ -59,7 +57,9 @@ public void integrate( @Override public void onPostInsert(PostInsertEvent event) { - evictCache( event.getEntity(), event.getPersister(), event.getSession(), null ); + if ( event.getSession() instanceof EventSource eventSource ) { + evictCache( event.getEntity(), event.getPersister(), eventSource, null ); + } } @Override @@ -69,12 +69,16 @@ public boolean requiresPostCommitHandling(EntityPersister persister) { @Override public void onPostDelete(PostDeleteEvent event) { - evictCache( event.getEntity(), event.getPersister(), event.getSession(), null ); + if ( event.getSession() instanceof EventSource eventSource ) { + evictCache( event.getEntity(), event.getPersister(), eventSource, null ); + } } @Override public void onPostUpdate(PostUpdateEvent event) { - evictCache( event.getEntity(), event.getPersister(), event.getSession(), event.getOldState() ); + if ( event.getSession() instanceof EventSource eventSource ) { + evictCache( event.getEntity(), event.getPersister(), eventSource, event.getOldState() ); + } } private void integrate(SessionFactoryImplementor sessionFactory) { @@ -124,7 +128,7 @@ private void evictCollection( final var cacheAccessStrategy = collectionPersister.getCacheAccessStrategy(); final var softLock = cacheAccessStrategy.lockRegion(); session.getActionQueue() - .registerProcess( (success, s) -> cacheAccessStrategy.unlockRegion( softLock ) ); + .registerCallback( (success, s) -> cacheAccessStrategy.unlockRegion( softLock ) ); } } } @@ -182,7 +186,7 @@ private void evict(Object id, CollectionPersister collectionPersister, EventSour final var evictCacheAction = new CollectionEvictCacheAction( collectionPersister, null, id, session ); evictCacheAction.execute(); - session.getActionQueue().registerProcess( evictCacheAction.getAfterTransactionCompletionProcess() ); + session.getActionQueue().registerCallback( evictCacheAction.getAfterTransactionCompletionProcess() ); } //execute the same process as invalidation with collection operations diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java index 75b96acaf823..5cf5ab687068 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java @@ -45,7 +45,7 @@ public void lock(Object id, Object version, Object object, int timeout, EventSou } // final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object ); // Register the EntityIncrementVersionProcess action to run just prior to transaction commit. - session.getActionQueue().registerProcess( new EntityIncrementVersionProcess( object ) ); + session.getActionQueue().registerCallback( new EntityIncrementVersionProcess( object ) ); } protected LockMode getLockMode() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java index d656e00c56d7..79337885d354 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java @@ -43,7 +43,7 @@ public void lock(Object id, Object version, Object object, int timeout, EventSou throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" ); } // Register the EntityVerifyVersionProcess action to run just prior to transaction commit. - session.getActionQueue().registerProcess( new EntityVerifyVersionProcess( object ) ); + session.getActionQueue().registerCallback( new EntityVerifyVersionProcess( object ) ); } protected LockMode getLockMode() { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java new file mode 100644 index 000000000000..ff6b308a3cc6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java @@ -0,0 +1,160 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation; + +import org.hibernate.CacheMode; +import org.hibernate.ConnectionAcquisitionMode; +import org.hibernate.ConnectionReleaseMode; +import org.hibernate.Incubating; +import org.hibernate.Interceptor; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.SharedSessionContract; +import org.hibernate.StatelessSession; + +import java.sql.Connection; +import java.util.function.UnaryOperator; + +/** + * Common options for builders of {@linkplain Session stateful} + * and {@linkplain StatelessSession stateless} sessions. + * + * @since 7.2 + * + * @author Steve Ebersole + */ +@Incubating +public interface CommonBuilder { + + /** + * Adds a specific connection to the session options. + * + * @param connection The connection to use. + * + * @return {@code this}, for method chaining + */ + CommonBuilder connection(Connection connection); + + /** + * Specifies the connection handling modes for the session. + *

+ * Note that if {@link ConnectionAcquisitionMode#IMMEDIATELY} is specified, + * then the release mode must be {@link ConnectionReleaseMode#ON_CLOSE}. + * + * @return {@code this}, for method chaining + * + * @since 7.0 + */ + CommonBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); + + /** + * Adds a specific interceptor to the session options. + * + * @param interceptor The interceptor to use. + * + * @return {@code this}, for method chaining + */ + CommonBuilder interceptor(Interceptor interceptor); + + /** + * Signifies that no {@link Interceptor} should be used. + *

+ * By default, if no {@code Interceptor} is explicitly specified, the + * {@code Interceptor} associated with the {@link SessionFactory} is + * inherited by the new session. + *

+ * Calling {@link #interceptor(Interceptor)} with null has the same effect. + * + * @return {@code this}, for method chaining + */ + CommonBuilder noInterceptor(); + + /** + * Applies the given statement inspection function to the session. + * + * @param operator An operator which accepts a SQL string, returning + * a processed SQL string to be used by Hibernate instead of the given + * original SQL. The operator may simply return the original SQL. + * + * @return {@code this}, for method chaining + */ + CommonBuilder statementInspector(UnaryOperator operator); + + /** + * Signifies that no SQL statement inspector should be used. + *

+ * By default, if no inspector is explicitly specified, the + * inspector associated with the {@link SessionFactory} is + * inherited by the new session. + *

+ * Calling {@link #interceptor(Interceptor)} with null has the same effect. + * + * @return {@code this}, for method chaining + */ + CommonBuilder noStatementInspector(); + + /** + * Define the tenant identifier to be associated with the opened session. + * + * @param tenantIdentifier The tenant identifier. + * + * @return {@code this}, for method chaining + */ + CommonBuilder tenantIdentifier(Object tenantIdentifier); + + /** + * Specify a {@linkplain Session#isDefaultReadOnly read-only mode} + * for the session. If a session is created in read-only mode, then + * {@link Connection#setReadOnly} is called when a JDBC connection + * is obtained. + *

+ * Furthermore, if read/write replication is in use, then: + *

+ *

+ * When read/write replication is in use, it's strongly recommended + * that the session be created with the {@linkplain #initialCacheMode + * initial cache mode} set to {@link CacheMode#GET}, to avoid writing + * stale data read from a read-only replica to the second-level cache. + * Hibernate cannot possibly guarantee that data read from a read-only + * replica is up to date. + *

+ * When read/write replication is in use, it's possible that an item + * read from the second-level cache might refer to data which does not + * yet exist in the read-only replica. In this situation, an exception + * occurs when the association is fetched. To completely avoid this + * possibility, the {@linkplain #initialCacheMode initial cache mode} + * must be set to {@link CacheMode#IGNORE}. However, it's also usually + * possible to structure data access code in a way which eliminates + * this possibility. + *

+ * If a session is created in read-only mode, then it cannot be + * changed to read-write mode, and any call to + * {@link Session#setDefaultReadOnly(boolean)} with fail. On the + * other hand, if a session is created in read-write mode, then it + * may later be switched to read-only mode, but all database access + * is directed to the writable replica. + * + * @return {@code this}, for method chaining + * @since 7.2 + * + * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#getReadOnlyConnection(Object) + * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#releaseReadOnlyConnection(Object, Connection) + */ + @Incubating + CommonBuilder readOnly(boolean readOnly); + + /** + * Specify the initial {@link CacheMode} for the session. + * + * @return {@code this}, for method chaining + * @since 7.2 + * + * @see SharedSessionContract#getCacheMode() + */ + CommonBuilder initialCacheMode(CacheMode cacheMode); +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java new file mode 100644 index 000000000000..81777e43b8d4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation; + +import org.hibernate.CacheMode; +import org.hibernate.Incubating; +import org.hibernate.Interceptor; +import org.hibernate.Session; +import org.hibernate.StatelessSession; + +import java.util.function.UnaryOperator; + +/** + * Common options for builders of {@linkplain Session stateful} + * and {@linkplain StatelessSession stateless} sessions + * which share state from an underlying session. + * + * @since 7.2 + * + * @author Steve Ebersole + */ +@Incubating +public interface CommonSharedBuilder extends CommonBuilder { + + /** + * Signifies that the connection from the original session should be used to create the new session. + * + * @return {@code this}, for method chaining + */ + CommonSharedBuilder connection(); + + /** + * Signifies the interceptor from the original session should be used to create the new session. + * + * @return {@code this}, for method chaining + */ + CommonSharedBuilder interceptor(); + + /** + * Signifies that the SQL {@linkplain org.hibernate.resource.jdbc.spi.StatementInspector statement inspector} + * from the original session should be used. + */ + CommonSharedBuilder statementInspector(); + + /** + * Signifies that no SQL {@linkplain org.hibernate.resource.jdbc.spi.StatementInspector statement inspector} + * should be used. + */ + CommonSharedBuilder noStatementInspector(); + + @Override + CommonSharedBuilder interceptor(Interceptor interceptor); + + @Override + CommonSharedBuilder noInterceptor(); + + @Override + CommonSharedBuilder statementInspector(UnaryOperator operator); + + @Override + CommonSharedBuilder readOnly(boolean readOnly); + + @Override + CommonSharedBuilder initialCacheMode(CacheMode cacheMode); + + @Override + CommonSharedBuilder tenantIdentifier(Object tenantIdentifier); +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/AbstractCommonBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/AbstractCommonBuilder.java new file mode 100644 index 000000000000..66e4ed54068a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/AbstractCommonBuilder.java @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.internal; + +import org.hibernate.CacheMode; +import org.hibernate.ConnectionAcquisitionMode; +import org.hibernate.ConnectionReleaseMode; +import org.hibernate.Interceptor; +import org.hibernate.engine.creation.CommonBuilder; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import java.sql.Connection; +import java.util.function.UnaryOperator; + +/** + * Base support for session builders. + * + * @author Steve Ebersole + */ +public abstract class AbstractCommonBuilder implements CommonBuilder { + protected final SessionFactoryImpl sessionFactory; + + protected StatementInspector statementInspector; + protected Interceptor interceptor; + protected boolean explicitNoInterceptor; + protected Connection connection; + protected PhysicalConnectionHandlingMode connectionHandlingMode; + protected Object tenantIdentifier; + protected boolean readOnly; + protected CacheMode cacheMode; + + public AbstractCommonBuilder(SessionFactoryImpl sessionFactory) { + this.sessionFactory = sessionFactory; + + final var options = sessionFactory.getSessionFactoryOptions(); + statementInspector = options.getStatementInspector(); + cacheMode = options.getInitialSessionCacheMode(); + tenantIdentifier = sessionFactory.resolveTenantIdentifier(); + connectionHandlingMode = options.getPhysicalConnectionHandlingMode(); + } + + protected abstract T getThis(); + + @Override + public T connection(Connection connection) { + this.connection = connection; + return getThis(); + } + + @Override + public T connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { + this.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret( acquisitionMode, releaseMode ); + return getThis(); + } + + @Override + public T interceptor(Interceptor interceptor) { + if ( interceptor == null ) { + noInterceptor(); + } + else { + this.interceptor = interceptor; + this.explicitNoInterceptor = false; + } + return getThis(); + } + + @Override + public T noInterceptor() { + this.interceptor = null; + this.explicitNoInterceptor = true; + return getThis(); + } + + @Override + public T tenantIdentifier(Object tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + return getThis(); + } + + @Override + public T readOnly(boolean readOnly) { + this.readOnly = readOnly; + return getThis(); + } + + @Override + public T initialCacheMode(CacheMode cacheMode) { + this.cacheMode = cacheMode; + return getThis(); + } + + @Override + public T statementInspector(UnaryOperator operator) { + if ( operator == null ) { + noStatementInspector(); + } + else { + this.statementInspector = operator::apply; + } + return getThis(); + } + + @Override + public T noStatementInspector() { + this.statementInspector = null; + return getThis(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionBuilderImpl.java new file mode 100644 index 000000000000..2071d6a3bce9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionBuilderImpl.java @@ -0,0 +1,259 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.internal; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionEventListener; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.SessionImpl; +import org.hibernate.internal.SessionOwnerBehavior; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.jboss.logging.Logger; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; + +import static java.util.Collections.addAll; + +/** + * SessionBuilder implementation. + * + * @author Steve Ebersole + */ +public class SessionBuilderImpl + extends AbstractCommonBuilder + implements SessionBuilderImplementor, SessionCreationOptions { + private static final Logger log = CoreLogging.logger( SessionBuilderImpl.class ); + + private boolean autoJoinTransactions = true; + private FlushMode flushMode; + private boolean autoClose; + private boolean autoClear; + private boolean identifierRollback; + private TimeZone jdbcTimeZone; + private final int defaultBatchFetchSize; + private final boolean subselectFetchEnabled; + + // 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 + private final SessionOwnerBehavior sessionOwnerBehavior = SessionOwnerBehavior.LEGACY_NATIVE; + + public SessionBuilderImpl(SessionFactoryImpl sessionFactory) { + super( sessionFactory ); + + // set up default builder values... + final var options = sessionFactory.getSessionFactoryOptions(); + statementInspector = options.getStatementInspector(); + connectionHandlingMode = options.getPhysicalConnectionHandlingMode(); + autoClose = options.isAutoCloseSessionEnabled(); + defaultBatchFetchSize = options.getDefaultBatchFetchSize(); + subselectFetchEnabled = options.isSubselectFetchEnabled(); + identifierRollback = options.isIdentifierRollbackEnabled(); + cacheMode = options.getInitialSessionCacheMode(); + + tenantIdentifier = sessionFactory.resolveTenantIdentifier(); + jdbcTimeZone = options.getJdbcTimeZone(); + } + + @Override + protected SessionBuilderImplementor getThis() { + return this; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SessionCreationOptions + + + @Override + public boolean shouldAutoJoinTransactions() { + return autoJoinTransactions; + } + + @Override + public FlushMode getInitialSessionFlushMode() { + return flushMode; + } + + @Override + public boolean isSubselectFetchEnabled() { + return subselectFetchEnabled; + } + + @Override + public int getDefaultBatchFetchSize() { + return defaultBatchFetchSize; + } + + @Override + public boolean shouldAutoClose() { + return autoClose; + } + + @Override + public boolean shouldAutoClear() { + return autoClear; + } + + @Override + public Connection getConnection() { + return connection; + } + + @Override + public Interceptor getInterceptor() { + return SessionFactoryImpl.configuredInterceptor( interceptor, explicitNoInterceptor, + sessionFactory.getSessionFactoryOptions() ); + } + + @Override + public StatementInspector getStatementInspector() { + return statementInspector; + } + + @Override + public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() { + return connectionHandlingMode; + } + + @Override + public String getTenantIdentifier() { + return tenantIdentifier != null + ? sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ) + : null; + } + + @Override + public Object getTenantIdentifierValue() { + return tenantIdentifier; + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + @Override + public CacheMode getInitialCacheMode() { + return cacheMode; + } + + @Override + public boolean isIdentifierRollbackEnabled() { + return identifierRollback; + } + + @Override + public TimeZone getJdbcTimeZone() { + return jdbcTimeZone; + } + + @Override + public List getCustomSessionEventListener() { + return listeners; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SessionBuilder + + @Override + public SessionImpl openSession() { + log.tracef( "Opening Hibernate Session. tenant=%s", tenantIdentifier ); + return new SessionImpl( sessionFactory, this ); + } + + @Override + @Deprecated + public SessionBuilderImplementor statementInspector(StatementInspector statementInspector) { + this.statementInspector = statementInspector; + return this; + } + + @Override + @Deprecated + public SessionBuilderImplementor connectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) { + this.connectionHandlingMode = connectionHandlingMode; + return this; + } + + @Override + public SessionBuilderImplementor autoJoinTransactions(boolean autoJoinTransactions) { + this.autoJoinTransactions = autoJoinTransactions; + return this; + } + + @Override + public SessionBuilderImplementor autoClose(boolean autoClose) { + this.autoClose = autoClose; + return this; + } + + @Override + public SessionBuilderImplementor autoClear(boolean autoClear) { + this.autoClear = autoClear; + return this; + } + + @Override + public SessionBuilderImplementor flushMode(FlushMode flushMode) { + this.flushMode = flushMode; + return this; + } + + @Override + @Deprecated(forRemoval = true) + public SessionBuilderImplementor tenantIdentifier(String tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + return this; + } + + @Override + public SessionBuilderImplementor identifierRollback(boolean identifierRollback) { + this.identifierRollback = identifierRollback; + return this; + } + + @Override + public SessionBuilderImplementor eventListeners(SessionEventListener... listeners) { + if ( this.listeners == null ) { + final var baselineListeners = + sessionFactory.getSessionFactoryOptions().buildSessionEventListeners(); + this.listeners = new ArrayList<>( baselineListeners.length + listeners.length ); + addAll( this.listeners, baselineListeners ); + } + addAll( this.listeners, listeners ); + return this; + } + + @Override + public SessionBuilderImplementor clearEventListeners() { + if ( listeners == null ) { + //Needs to initialize explicitly to an empty list as otherwise "null" implies the default listeners will be applied + listeners = new ArrayList<>( 3 ); + } + else { + listeners.clear(); + } + return this; + } + + @Override + public SessionBuilderImplementor jdbcTimeZone(TimeZone timeZone) { + jdbcTimeZone = timeZone; + return this; + } + + +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionCreationOptions.java similarity index 76% rename from hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java rename to hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionCreationOptions.java index 98ee29bcb9a8..2027f4c9dabb 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionCreationOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SessionCreationOptions.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.internal; +package org.hibernate.engine.creation.internal; import java.sql.Connection; import java.util.List; @@ -12,16 +12,17 @@ import org.hibernate.FlushMode; import org.hibernate.Interceptor; import org.hibernate.SessionEventListener; +import org.hibernate.engine.creation.CommonBuilder; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; -import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ExceptionMapper; /** + * Options, specified through various {@linkplain CommonBuilder builders}, + * used when creating sessions. + * * @author Steve Ebersole */ public interface SessionCreationOptions { - // todo : (5.2) review this. intended as a consolidation of the options needed to create a Session - // comes from building a Session and a EntityManager boolean shouldAutoJoinTransactions(); @@ -60,9 +61,4 @@ public interface SessionCreationOptions { * or null if this Session is being created with the default list. */ List getCustomSessionEventListener(); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // deprecations - - ExceptionMapper getExceptionMapper(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionBuilderImpl.java new file mode 100644 index 000000000000..3d92e28d1f49 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionBuilderImpl.java @@ -0,0 +1,281 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.internal; + +import org.hibernate.CacheMode; +import org.hibernate.ConnectionAcquisitionMode; +import org.hibernate.ConnectionReleaseMode; +import org.hibernate.FlushMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionEventListener; +import org.hibernate.SessionException; +import org.hibernate.Transaction; +import org.hibernate.engine.creation.spi.SharedSessionBuilderImplementor; +import org.hibernate.engine.internal.TransactionCompletionCallbacksImpl; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.SessionImpl; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.resource.transaction.spi.TransactionCoordinator; + +import java.sql.Connection; +import java.util.TimeZone; +import java.util.function.UnaryOperator; + +/** + * @author Steve Ebersole + */ +public class SharedSessionBuilderImpl + extends SessionBuilderImpl + implements SharedSessionBuilderImplementor, SharedSessionCreationOptions { + private final SessionImpl session; + + private boolean shareTransactionContext; + private boolean tenantIdChanged; + private boolean readOnlyChanged; + + public SharedSessionBuilderImpl(SessionImpl session) { + super( (SessionFactoryImpl) session.getFactory() ); + this.session = session; + super.tenantIdentifier( session.getTenantIdentifierValue() ); + super.identifierRollback( session.isIdentifierRollbackEnabled() ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SharedSessionBuilder + + @Override + public SessionImpl openSession() { + if ( session.getFactory().getSessionFactoryOptions().isMultiTenancyEnabled() ) { + if ( shareTransactionContext ) { + if ( tenantIdChanged ) { + throw new SessionException( + "Cannot redefine the tenant identifier on a child session if the connection is reused" ); + } + if ( readOnlyChanged ) { + throw new SessionException( + "Cannot redefine the read-only mode on a child session if the connection is reused" ); + } + } + } + return super.openSession(); + } + + + @Override + @Deprecated(forRemoval = true) + public SharedSessionBuilderImplementor tenantIdentifier(String tenantIdentifier) { + tenantIdentifier( (Object) tenantIdentifier ); + return this; + } + + @Override + public SharedSessionBuilderImplementor tenantIdentifier(Object tenantIdentifier) { + super.tenantIdentifier( tenantIdentifier ); + tenantIdChanged = true; + return this; + } + + @Override + public SharedSessionBuilderImplementor readOnly(boolean readOnly) { + super.readOnly( readOnly ); + readOnlyChanged = true; + return this; + } + + @Override + public SharedSessionBuilderImplementor connection() { + this.shareTransactionContext = true; + return this; + } + + @Override + public SharedSessionBuilderImplementor interceptor() { + interceptor = session.getInterceptor(); + return this; + } + + @Override + public SharedSessionBuilderImplementor statementInspector() { + statementInspector = session.getJdbcSessionContext().getStatementInspector(); + return this; + } + + private PhysicalConnectionHandlingMode getConnectionHandlingMode() { + return session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode(); + } + + @Override + @Deprecated(since = "6.0") + public SharedSessionBuilderImplementor connectionReleaseMode() { + final PhysicalConnectionHandlingMode handlingMode = + PhysicalConnectionHandlingMode.interpret( ConnectionAcquisitionMode.AS_NEEDED, + getConnectionHandlingMode().getReleaseMode() ); + connectionHandlingMode( handlingMode ); + return this; + } + + @Override + public SharedSessionBuilderImplementor connectionHandlingMode() { + connectionHandlingMode( getConnectionHandlingMode() ); + return this; + } + + @Override + public SharedSessionBuilderImplementor autoJoinTransactions() { + super.autoJoinTransactions( session.shouldAutoJoinTransaction() ); + return this; + } + + @Override + public SharedSessionBuilderImplementor autoJoinTransactions(boolean autoJoinTransactions) { + super.autoJoinTransactions( autoJoinTransactions ); + return this; + } + + @Override + public SharedSessionBuilderImplementor autoClose(boolean autoClose) { + super.autoClose( autoClose ); + return this; + } + + @Override + public SharedSessionBuilderImplementor flushMode() { + flushMode( session.getHibernateFlushMode() ); + return this; + } + + @Override + public SharedSessionBuilderImplementor autoClose() { + autoClose( session.isAutoCloseSessionEnabled() ); + return this; + } + + @Override + public SharedSessionBuilderImplementor identifierRollback(boolean identifierRollback) { + super.identifierRollback( identifierRollback ); + return this; + } + + @Override + public SharedSessionBuilderImplementor jdbcTimeZone(TimeZone timeZone) { + super.jdbcTimeZone( timeZone ); + return this; + } + + @Override + public SharedSessionBuilderImplementor clearEventListeners() { + super.clearEventListeners(); + return this; + } + + @Override + public SharedSessionBuilderImplementor flushMode(FlushMode flushMode) { + super.flushMode( flushMode ); + return this; + } + + @Override + public SharedSessionBuilderImplementor autoClear(boolean autoClear) { + super.autoClear( autoClear ); + return this; + } + + @Override + @Deprecated + public SharedSessionBuilderImplementor statementInspector(StatementInspector statementInspector) { + super.statementInspector( statementInspector ); + return this; + } + + @Override + public SharedSessionBuilderImplementor statementInspector(UnaryOperator operator) { + super.statementInspector( operator ); + return this; + } + + @Override + @Deprecated + public SharedSessionBuilderImplementor connectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) { + super.connectionHandlingMode( connectionHandlingMode ); + return this; + } + + @Override + @Deprecated + public SharedSessionBuilderImplementor connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { + super.connectionHandling( acquisitionMode, releaseMode ); + return this; + } + + @Override + public SharedSessionBuilderImplementor eventListeners(SessionEventListener... listeners) { + super.eventListeners( listeners ); + return this; + } + + @Override + public SharedSessionBuilderImplementor connection(Connection connection) { + super.connection( connection ); + return this; + } + + @Override + public SharedSessionBuilderImplementor interceptor(Interceptor interceptor) { + super.interceptor( interceptor ); + return this; + } + + @Override + public SharedSessionBuilderImplementor noInterceptor() { + super.noInterceptor(); + return this; + } + + @Override + public SharedSessionBuilderImplementor initialCacheMode(CacheMode cacheMode) { + super.initialCacheMode( cacheMode ); + return this; + } + + @Override + public SharedSessionBuilderImplementor noStatementInspector() { + super.noStatementInspector(); + return this; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SharedSessionCreationOptions + + @Override + public boolean isTransactionCoordinatorShared() { + return shareTransactionContext; + } + + @Override + public TransactionCoordinator getTransactionCoordinator() { + return shareTransactionContext ? session.getTransactionCoordinator() : null; + } + + @Override + public JdbcCoordinator getJdbcCoordinator() { + return shareTransactionContext ? session.getJdbcCoordinator() : null; + } + + @Override + public Transaction getTransaction() { + return shareTransactionContext ? session.getCurrentTransaction() : null; + } + + @Override + public TransactionCompletionCallbacksImpl getTransactionCompletionCallbacks() { + return shareTransactionContext + ? session.getActionQueue().getTransactionCompletionCallbacks() + : null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SharedSessionCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionCreationOptions.java similarity index 79% rename from hibernate-core/src/main/java/org/hibernate/internal/SharedSessionCreationOptions.java rename to hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionCreationOptions.java index b66b1de904d3..72c6ccd42e4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SharedSessionCreationOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionCreationOptions.java @@ -2,11 +2,11 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.internal; +package org.hibernate.engine.creation.internal; import org.hibernate.Transaction; +import org.hibernate.engine.internal.TransactionCompletionCallbacksImpl; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; -import org.hibernate.engine.spi.ActionQueue; import org.hibernate.resource.transaction.spi.TransactionCoordinator; /** @@ -23,5 +23,5 @@ public interface SharedSessionCreationOptions extends SessionCreationOptions { TransactionCoordinator getTransactionCoordinator(); JdbcCoordinator getJdbcCoordinator(); Transaction getTransaction(); - ActionQueue.TransactionCompletionProcesses getTransactionCompletionProcesses(); + TransactionCompletionCallbacksImpl getTransactionCompletionCallbacks(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java new file mode 100644 index 000000000000..27748b74cf5b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java @@ -0,0 +1,131 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.internal; + +import org.hibernate.CacheMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionException; +import org.hibernate.SharedStatelessSessionBuilder; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.StatelessSessionImplementor; +import org.hibernate.internal.CommonSharedSessionCreationOptions; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.StatelessSessionImpl; +import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.resource.transaction.spi.TransactionCoordinator; + +import java.util.Objects; + +/** + * Builder for shared {@linkplain StatelessSessionImplementor stateless} sessions. + * Exposes the builder state via its {@linkplain CommonSharedSessionCreationOptions} implementation + * for use when creating the shared stateless session. + * + * @author Steve Ebersole + */ +public class SharedStatelessSessionBuilderImpl + extends AbstractCommonBuilder + implements SharedStatelessSessionBuilder, CommonSharedSessionCreationOptions { + + protected final SharedSessionContractImplementor original; + protected boolean shareTransactionContext; + + public SharedStatelessSessionBuilderImpl(SharedSessionContractImplementor original) { + super( (SessionFactoryImpl) original.getSessionFactory() ); + this.original = original; + + this.tenantIdentifier = original.getTenantIdentifierValue(); + this.interceptor = original.getSessionFactory().getSessionFactoryOptions().getInterceptor(); + this.statementInspector = original.getSessionFactory().getSessionFactoryOptions().getStatementInspector(); + } + + @Override + protected SharedStatelessSessionBuilder getThis() { + return this; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SharedStatelessSessionBuilder + + @Override + public StatelessSessionImplementor open() { + if ( original.getSessionFactory().getSessionFactoryOptions().isMultiTenancyEnabled() ) { + if ( shareTransactionContext ) { + assert original.getTenantIdentifierValue() != null; + if ( Objects.equals( original.getTenantIdentifierValue(), tenantIdentifier ) ) { + throw new SessionException( "Cannot redefine the tenant identifier on a child session if the connection is reused" ); + } + } + } + return new StatelessSessionImpl( original.getSessionFactory(), this ); + } + + @Override + public SharedStatelessSessionBuilder connection() { + shareTransactionContext = true; + return this; + } + + @Override + public SharedStatelessSessionBuilder interceptor() { + interceptor = original.getInterceptor(); + return this; + } + + @Override + public SharedStatelessSessionBuilder statementInspector() { + this.statementInspector = original.getJdbcSessionContext().getStatementInspector(); + return this; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CommonSharedSessionCreationOptions + + @Override + public Interceptor getInterceptor() { + return interceptor; + } + + @Override + public StatementInspector getStatementInspector() { + return statementInspector; + } + + @Override + public Object getTenantIdentifierValue() { + return tenantIdentifier; + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + @Override + public CacheMode getInitialCacheMode() { + return cacheMode; + } + + @Override + public boolean isTransactionCoordinatorShared() { + return shareTransactionContext; + } + + @Override + public TransactionCoordinator getTransactionCoordinator() { + return isTransactionCoordinatorShared() + ? original.getTransactionCoordinator() + : null; + } + + @Override + public JdbcCoordinator getJdbcCoordinator() { + return isTransactionCoordinatorShared() + ? original.getJdbcCoordinator() + : null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java new file mode 100644 index 000000000000..dc383f7a6190 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.internal; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionEventListener; +import org.hibernate.StatelessSession; +import org.hibernate.StatelessSessionBuilder; +import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.StatelessSessionImpl; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import java.sql.Connection; +import java.util.List; +import java.util.TimeZone; + +/** + * @author Steve Ebersole + */ +public class StatelessSessionBuilderImpl + extends AbstractCommonBuilder + implements StatelessSessionBuilder, SessionCreationOptions { + + public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) { + super( sessionFactory ); + } + + @Override + protected StatelessSessionBuilder getThis() { + return this; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // StatelessSessionBuilder + + @Override + public StatelessSession openStatelessSession() { + return new StatelessSessionImpl( sessionFactory, this ); + } + + @Override + @Deprecated + public StatelessSessionBuilder tenantIdentifier(String tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + return this; + } + + @Override + @Deprecated + public StatelessSessionBuilder statementInspector(StatementInspector statementInspector) { + this.statementInspector = statementInspector; + return this; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SessionCreationOptions + + @Override + public boolean shouldAutoJoinTransactions() { + return true; + } + + @Override + public FlushMode getInitialSessionFlushMode() { + return FlushMode.ALWAYS; + } + + @Override + public boolean isSubselectFetchEnabled() { + return false; + } + + @Override + public int getDefaultBatchFetchSize() { + return -1; + } + + @Override + public boolean shouldAutoClose() { + return false; + } + + @Override + public boolean shouldAutoClear() { + return false; + } + + @Override + public Connection getConnection() { + return connection; + } + + @Override + public Interceptor getInterceptor() { + return SessionFactoryImpl.configuredInterceptor( interceptor, explicitNoInterceptor, + sessionFactory.getSessionFactoryOptions() ); + } + + @Override + public boolean isIdentifierRollbackEnabled() { + // identifier rollback is not yet implemented for StatelessSessions + return false; + } + + @Override + public StatementInspector getStatementInspector() { + return statementInspector; + } + + @Override + public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() { + return connectionHandlingMode; + } + + @Override + public String getTenantIdentifier() { + return tenantIdentifier == null ? null + : sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ); + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + @Override + public CacheMode getInitialCacheMode() { + return cacheMode; + } + + @Override + public Object getTenantIdentifierValue() { + return tenantIdentifier; + } + + @Override + public TimeZone getJdbcTimeZone() { + return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone(); + } + + @Override + public List getCustomSessionEventListener() { + return null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/package-info.java new file mode 100644 index 000000000000..9015b03f1fa6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ + +/** + * Support for the creation of {@linkplain org.hibernate.Session stateful} + * and {@linkplain org.hibernate.StatelessSession stateless} sessions. + * + * @author Steve Ebersole + */ +package org.hibernate.engine.creation; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SessionBuilderImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SessionBuilderImplementor.java new file mode 100644 index 000000000000..9fc437abdecd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SessionBuilderImplementor.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.spi; + +import org.hibernate.CacheMode; +import org.hibernate.ConnectionAcquisitionMode; +import org.hibernate.ConnectionReleaseMode; +import org.hibernate.FlushMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionBuilder; +import org.hibernate.SessionEventListener; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import java.sql.Connection; +import java.util.TimeZone; +import java.util.function.UnaryOperator; + +/** + * Defines the internal contract between the {@link SessionBuilder} and + * other parts of Hibernate. + * + * @see SessionBuilder + * + * @author Gail Badner + */ +public interface SessionBuilderImplementor extends SessionBuilder { + @Override + SessionImplementor openSession(); + + @Override + SessionBuilderImplementor interceptor(Interceptor interceptor); + + @Override + SessionBuilderImplementor noInterceptor(); + + @Override + SessionBuilderImplementor statementInspector(UnaryOperator operator); + + @Override + SessionBuilderImplementor statementInspector(StatementInspector statementInspector); + + @Override + SessionBuilderImplementor connection(Connection connection); + + @Override + SessionBuilderImplementor connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); + + @Override + SessionBuilderImplementor connectionHandlingMode(PhysicalConnectionHandlingMode mode); + + @Override + SessionBuilderImplementor autoJoinTransactions(boolean autoJoinTransactions); + + @Override + SessionBuilderImplementor autoClear(boolean autoClear); + + @Override + SessionBuilderImplementor flushMode(FlushMode flushMode); + + @Override + SessionBuilderImplementor tenantIdentifier(String tenantIdentifier); + + @Override + SessionBuilderImplementor tenantIdentifier(Object tenantIdentifier); + + @Override + SessionBuilderImplementor readOnly(boolean readOnly); + + @Override + SessionBuilderImplementor initialCacheMode(CacheMode cacheMode); + + @Override + SessionBuilderImplementor eventListeners(SessionEventListener... listeners); + + @Override + SessionBuilderImplementor clearEventListeners(); + + @Override + SessionBuilderImplementor jdbcTimeZone(TimeZone timeZone); + + @Override + SessionBuilderImplementor autoClose(boolean autoClose); + + @Override + SessionBuilderImplementor identifierRollback(boolean identifierRollback); + + @Override + SessionBuilderImplementor noStatementInspector(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SharedSessionBuilderImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SharedSessionBuilderImplementor.java new file mode 100644 index 000000000000..72b4c78b1bed --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/spi/SharedSessionBuilderImplementor.java @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.creation.spi; + +import org.hibernate.CacheMode; +import org.hibernate.ConnectionAcquisitionMode; +import org.hibernate.ConnectionReleaseMode; +import org.hibernate.FlushMode; +import org.hibernate.Interceptor; +import org.hibernate.SessionEventListener; +import org.hibernate.SharedSessionBuilder; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import java.sql.Connection; +import java.util.TimeZone; +import java.util.function.UnaryOperator; + +/** + * @author Steve Ebersole + */ +public interface SharedSessionBuilderImplementor extends SharedSessionBuilder, SessionBuilderImplementor { + @Override + SharedSessionBuilderImplementor interceptor(Interceptor interceptor); + + @Override + SharedSessionBuilderImplementor noInterceptor(); + + @Override + SharedSessionBuilderImplementor statementInspector(UnaryOperator operator); + + @Override + SharedSessionBuilderImplementor statementInspector(StatementInspector statementInspector); + + @Override + SharedSessionBuilderImplementor tenantIdentifier(Object tenantIdentifier); + + @Override + SharedSessionBuilderImplementor readOnly(boolean readOnly); + + @Override + SharedSessionBuilderImplementor initialCacheMode(CacheMode cacheMode); + + + @Override + SharedSessionBuilderImplementor connection(); + + @Override + SharedSessionBuilderImplementor interceptor(); + + @Override + SharedSessionBuilderImplementor connectionReleaseMode(); + + @Override + SharedSessionBuilderImplementor connectionHandlingMode(); + + @Override + SharedSessionBuilderImplementor autoJoinTransactions(); + + @Override + SharedSessionBuilderImplementor flushMode(); + + @Override + SharedSessionBuilderImplementor autoClose(); + + @Override + SharedSessionBuilderImplementor connectionHandlingMode(PhysicalConnectionHandlingMode mode); + + @Override + SharedSessionBuilderImplementor connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); + + @Override + SharedSessionBuilderImplementor autoClear(boolean autoClear); + + @Override + SharedSessionBuilderImplementor flushMode(FlushMode flushMode); + + @Override + SharedSessionBuilderImplementor tenantIdentifier(String tenantIdentifier); + + @Override + SharedSessionBuilderImplementor eventListeners(SessionEventListener... listeners); + + @Override + SharedSessionBuilderImplementor clearEventListeners(); + + @Override + SharedSessionBuilderImplementor jdbcTimeZone(TimeZone timeZone); + + @Override + SharedSessionBuilderImplementor connection(Connection connection); + + @Override + SharedSessionBuilderImplementor autoJoinTransactions(boolean autoJoinTransactions); + + @Override + SharedSessionBuilderImplementor autoClose(boolean autoClose); + + @Override + SharedSessionBuilderImplementor identifierRollback(boolean identifierRollback); + + @Override + SharedSessionBuilderImplementor statementInspector(); + + @Override + SharedSessionBuilderImplementor noStatementInspector(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java new file mode 100644 index 000000000000..6ee772b9d4a7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks.CompletionCallback; + +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * Collection of transaction completion {@linkplain CompletionCallback callbacks}. + * + * @author Steve Ebersole + */ +public abstract class AbstractTransactionCompletionProcessQueue { + protected SharedSessionContractImplementor session; + // Concurrency handling required when transaction completion process is dynamically registered + // inside event listener (HHH-7478). + protected ConcurrentLinkedQueue<@NonNull T> processes = new ConcurrentLinkedQueue<>(); + + protected AbstractTransactionCompletionProcessQueue(SharedSessionContractImplementor session) { + this.session = session; + } + + public void register(@Nullable T process) { + if ( process != null ) { + processes.add( process ); + } + } + + public boolean hasActions() { + return !processes.isEmpty(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java new file mode 100644 index 000000000000..348561417c0a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import org.hibernate.HibernateException; +import org.hibernate.cache.CacheException; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks.AfterCompletionCallback; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; + +import java.util.HashSet; +import java.util.Set; + +/** + * Encapsulates behavior needed for after transaction processing + */ +public class AfterTransactionCompletionProcessQueue + extends AbstractTransactionCompletionProcessQueue { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AfterTransactionCompletionProcessQueue.class ); + + private final Set querySpacesToInvalidate = new HashSet<>(); + + public AfterTransactionCompletionProcessQueue(SharedSessionContractImplementor session) { + super( session ); + } + + public void addSpaceToInvalidate(String space) { + querySpacesToInvalidate.add( space ); + } + + @Override + public boolean hasActions() { + return super.hasActions() || !querySpacesToInvalidate.isEmpty(); + } + + public void afterTransactionCompletion(boolean success) { + AfterCompletionCallback process; + while ( (process = processes.poll()) != null ) { + try { + process.doAfterTransactionCompletion( success, session ); + } + catch (CacheException ce) { + LOG.unableToReleaseCacheLock( ce ); + // continue loop + } + catch (Exception e) { + throw new HibernateException( + "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e ); + } + } + + final SessionFactoryImplementor factory = session.getFactory(); + if ( factory.getSessionFactoryOptions().isQueryCacheEnabled() ) { + factory.getCache().getTimestampsCache() + .invalidate( querySpacesToInvalidate.toArray( new String[0] ), session ); + } + querySpacesToInvalidate.clear(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java new file mode 100644 index 000000000000..ae6f81e63d18 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks.BeforeCompletionCallback; + +/** + * Encapsulates behavior needed for before transaction processing + */ +public class BeforeTransactionCompletionProcessQueue + extends AbstractTransactionCompletionProcessQueue { + + public BeforeTransactionCompletionProcessQueue(SharedSessionContractImplementor session) { + super( session ); + } + + public void beforeTransactionCompletion() { + BeforeCompletionCallback process; + while ( (process = processes.poll()) != null ) { + try { + process.doBeforeTransactionCompletion( session ); + } + catch (HibernateException he) { + throw he; + } + catch (Exception e) { + throw new HibernateException( + "Unable to perform beforeTransactionCompletion callback: " + e.getMessage(), e ); + } + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java index 1c0591af9d34..a6f495ea1977 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java @@ -320,7 +320,7 @@ private void cacheFromUpdate( ); } - session.asEventSource().getActionQueue().registerProcess( + session.asEventSource().getActionQueue().registerCallback( (success, sess) -> { cacheAccess.unlockItem( sess, previousCacheKey, removalLock ); if (success) { @@ -387,7 +387,7 @@ private void cacheFromInsert( ); } - session.asEventSource().getActionQueue().registerProcess( + session.asEventSource().getActionQueue().registerCallback( (success, sess) -> { if ( success ) { final boolean changed = cacheAccess.afterInsert( sess, cacheKey, id ); @@ -472,7 +472,7 @@ public void removeSharedResolution(Object id, Object naturalId, EntityMappingTyp final var persister = locatePersisterForKey( entityDescriptor.getEntityPersister() ); final Object naturalIdCacheKey = cacheAccess.generateCacheKey( naturalId, persister, session ); if ( delayToAfterTransactionCompletion ) { - session.asEventSource().getActionQueue().registerProcess( + session.asEventSource().getActionQueue().registerCallback( (success, sess) -> { if ( success ) { cacheAccess.evict( naturalIdCacheKey ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/TransactionCompletionCallbacksImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/TransactionCompletionCallbacksImpl.java new file mode 100644 index 000000000000..4499b55ee1b2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/TransactionCompletionCallbacksImpl.java @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks; + +/** + * @author Steve Ebersole + */ +public class TransactionCompletionCallbacksImpl implements TransactionCompletionCallbacks { + private final SharedSessionContractImplementor session; + + private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses; + private AfterTransactionCompletionProcessQueue afterTransactionProcesses; + + public TransactionCompletionCallbacksImpl(SharedSessionContractImplementor session) { + this.session = session; + } + + @Override + public void registerCallback(BeforeCompletionCallback process) { + if ( beforeTransactionProcesses == null ) { + beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); + } + beforeTransactionProcesses.register( process ); + } + + public boolean hasBeforeCompletionCallbacks() { + return beforeTransactionProcesses != null + && beforeTransactionProcesses.hasActions(); + } + + public void beforeTransactionCompletion() { + if ( beforeTransactionProcesses != null && beforeTransactionProcesses.hasActions() ) { + beforeTransactionProcesses.beforeTransactionCompletion(); + } + } + + @Override + public void registerCallback(AfterCompletionCallback process) { + if ( afterTransactionProcesses == null ) { + afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); + } + afterTransactionProcesses.register( process ); + } + + public boolean hasAfterCompletionCallbacks() { + return afterTransactionProcesses != null && afterTransactionProcesses.hasActions(); + } + + public void afterTransactionCompletion(boolean success) { + if ( afterTransactionProcesses != null && afterTransactionProcesses.hasActions() ) { + afterTransactionProcesses.afterTransactionCompletion( success ); + } + } + + public void addSpaceToInvalidate(String space) { + if ( afterTransactionProcesses == null ) { + afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); + } + afterTransactionProcesses.addSpaceToInvalidate( space ); +} + + public TransactionCompletionCallbacksImpl forSharing() { + if ( beforeTransactionProcesses == null ) { + beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); + } + if ( afterTransactionProcesses == null ) { + afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); + } + return this; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java index 6fe91e7a55e2..f77fcfb7689a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilder.java @@ -13,9 +13,9 @@ import org.hibernate.ConnectionReleaseMode; import org.hibernate.FlushMode; import org.hibernate.Interceptor; -import org.hibernate.Session; import org.hibernate.SessionBuilder; import org.hibernate.SessionEventListener; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -26,137 +26,143 @@ * @author Gunnar Morling * @author Guillaume Smet */ -public abstract class AbstractDelegatingSessionBuilder implements SessionBuilder { +public abstract class AbstractDelegatingSessionBuilder implements SessionBuilderImplementor { - private final SessionBuilder delegate; + private final SessionBuilderImplementor delegate; public AbstractDelegatingSessionBuilder(SessionBuilder delegate) { - this.delegate = delegate; + this.delegate = (SessionBuilderImplementor) delegate; } protected SessionBuilder getThis() { return this; } - protected SessionBuilder delegate() { + protected SessionBuilderImplementor delegate() { return delegate; } @Override - public Session openSession() { + public SessionImplementor openSession() { return delegate.openSession(); } @Override - public SessionBuilder interceptor(Interceptor interceptor) { + public SessionBuilderImplementor interceptor(Interceptor interceptor) { delegate.interceptor( interceptor ); return this; } @Override - public SessionBuilder noInterceptor() { + public SessionBuilderImplementor noInterceptor() { delegate.noInterceptor(); return this; } @Override @Deprecated - public SessionBuilder statementInspector(StatementInspector statementInspector) { + public SessionBuilderImplementor statementInspector(StatementInspector statementInspector) { delegate.statementInspector( statementInspector ); return this; } @Override - public SessionBuilder statementInspector(UnaryOperator operator) { + public SessionBuilderImplementor statementInspector(UnaryOperator operator) { delegate.statementInspector( operator ); return this; } @Override - public SessionBuilder connection(Connection connection) { + public SessionBuilderImplementor noStatementInspector() { + delegate.noStatementInspector(); + return this; + } + + @Override + public SessionBuilderImplementor connection(Connection connection) { delegate.connection( connection ); return this; } @Override - public SessionBuilder autoJoinTransactions(boolean autoJoinTransactions) { + public SessionBuilderImplementor autoJoinTransactions(boolean autoJoinTransactions) { delegate.autoJoinTransactions( autoJoinTransactions ); return this; } @Override - public SessionBuilder autoClose(boolean autoClose) { + public SessionBuilderImplementor autoClose(boolean autoClose) { delegate.autoClose( autoClose ); return this; } @Override @Deprecated(forRemoval = true) - public SessionBuilder tenantIdentifier(String tenantIdentifier) { + public SessionBuilderImplementor tenantIdentifier(String tenantIdentifier) { delegate.tenantIdentifier( tenantIdentifier ); return this; } @Override - public SessionBuilder tenantIdentifier(Object tenantIdentifier) { + public SessionBuilderImplementor tenantIdentifier(Object tenantIdentifier) { delegate.tenantIdentifier( tenantIdentifier ); return this; } @Override - public SessionBuilder readOnly(boolean readOnly) { + public SessionBuilderImplementor readOnly(boolean readOnly) { delegate.readOnly( readOnly ); return this; } @Override - public SessionBuilder initialCacheMode(CacheMode cacheMode) { + public SessionBuilderImplementor initialCacheMode(CacheMode cacheMode) { delegate.initialCacheMode( cacheMode ); return this; } @Override - public SessionBuilder eventListeners(SessionEventListener... listeners) { + public SessionBuilderImplementor eventListeners(SessionEventListener... listeners) { delegate.eventListeners( listeners ); return this; } @Override - public SessionBuilder clearEventListeners() { + public SessionBuilderImplementor clearEventListeners() { delegate.clearEventListeners(); return this; } @Override - public SessionBuilder jdbcTimeZone(TimeZone timeZone) { + public SessionBuilderImplementor jdbcTimeZone(TimeZone timeZone) { delegate.jdbcTimeZone(timeZone); return this; } @Override @Deprecated - public SessionBuilder connectionHandlingMode(PhysicalConnectionHandlingMode mode) { + public SessionBuilderImplementor connectionHandlingMode(PhysicalConnectionHandlingMode mode) { delegate.connectionHandlingMode( mode ); return this; } @Override - public SessionBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { + public SessionBuilderImplementor connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { delegate.connectionHandling( acquisitionMode, releaseMode ); return this; } @Override - public SessionBuilder autoClear(boolean autoClear) { + public SessionBuilderImplementor autoClear(boolean autoClear) { delegate.autoClear( autoClear ); return this; } @Override - public SessionBuilder flushMode(FlushMode flushMode) { + public SessionBuilderImplementor flushMode(FlushMode flushMode) { delegate.flushMode( flushMode ); return this; } @Override - public SessionBuilder identifierRollback(boolean identifierRollback) { + public SessionBuilderImplementor identifierRollback(boolean identifierRollback) { delegate.identifierRollback( identifierRollback ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilderImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilderImplementor.java index 7d6ce38f6aec..1cd8f8e490a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilderImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSessionBuilderImplementor.java @@ -4,6 +4,8 @@ */ package org.hibernate.engine.spi; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; + /** * Base class for {@link SessionBuilderImplementor} implementations that wish to implement only parts of that contract * themselves while forwarding other method invocations to a delegate instance. @@ -16,7 +18,4 @@ public AbstractDelegatingSessionBuilderImplementor(SessionBuilderImplementor del super( delegate ); } - protected SessionBuilderImplementor delegate() { - return (SessionBuilderImplementor) super.delegate(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java index 1a6de07f196c..e947fc22a6bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingSharedSessionBuilder.java @@ -8,13 +8,13 @@ import java.util.TimeZone; import java.util.function.UnaryOperator; +import org.hibernate.engine.creation.CommonSharedBuilder; import org.hibernate.CacheMode; import org.hibernate.ConnectionAcquisitionMode; import org.hibernate.ConnectionReleaseMode; import org.hibernate.FlushMode; import org.hibernate.Interceptor; import org.hibernate.Session; -import org.hibernate.SessionBuilder; import org.hibernate.SessionEventListener; import org.hibernate.SharedSessionBuilder; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; @@ -28,7 +28,6 @@ * @author Guillaume Smet */ public abstract class AbstractDelegatingSharedSessionBuilder implements SharedSessionBuilder { - private final SharedSessionBuilder delegate; public AbstractDelegatingSharedSessionBuilder(SharedSessionBuilder delegate) { @@ -103,11 +102,23 @@ public SharedSessionBuilder statementInspector(StatementInspector statementInspe } @Override - public SessionBuilder statementInspector(UnaryOperator operator) { + public SharedSessionBuilder statementInspector(UnaryOperator operator) { delegate.statementInspector( operator ); return this; } + @Override + public CommonSharedBuilder statementInspector() { + delegate.statementInspector(); + return this; + } + + @Override + public CommonSharedBuilder noStatementInspector() { + delegate.noStatementInspector(); + return this; + } + @Override public SharedSessionBuilder connection(Connection connection) { delegate.connection( connection ); 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 b65b407a8df3..ad6ca4f18805 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 @@ -17,7 +17,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; @@ -40,8 +39,8 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.action.spi.Executable; import org.hibernate.boot.spi.SessionFactoryOptions; -import org.hibernate.cache.CacheException; import org.hibernate.engine.internal.NonNullableTransientDependencies; +import org.hibernate.engine.internal.TransactionCompletionCallbacksImpl; import org.hibernate.event.spi.EventSource; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -55,7 +54,6 @@ import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.Type; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; @@ -70,7 +68,7 @@ * @author Gail Badner * @author Anton Marsden */ -public class ActionQueue { +public class ActionQueue implements TransactionCompletionCallbacks { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ActionQueue.class ); private final SessionImplementor session; @@ -102,8 +100,7 @@ public class ActionQueue { private transient boolean isTransactionCoordinatorShared; - private AfterTransactionCompletionProcessQueue afterTransactionProcesses; - private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses; + private TransactionCompletionCallbacksImpl transactionCompletionCallbacks;; // Extract this as a constant to perform efficient iterations: // method values() otherwise allocates a new array on each invocation. @@ -235,6 +232,7 @@ public void ensureInitialized(ActionQueue instance) { public ActionQueue(SessionImplementor session) { this.session = session; isTransactionCoordinatorShared = false; + transactionCompletionCallbacks = new TransactionCompletionCallbacksImpl( session ); } public void clear() { @@ -417,20 +415,18 @@ public void addAction(BulkOperationCleanupAction action) { } private void registerCleanupActions(Executable executable) { - if ( executable.getBeforeTransactionCompletionProcess() != null ) { - if ( beforeTransactionProcesses == null ) { - beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); - } - beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() ); + final BeforeTransactionCompletionProcess beforeCompletionCallback = executable.getBeforeTransactionCompletionProcess(); + if ( beforeCompletionCallback != null ) { + transactionCompletionCallbacks.registerCallback( beforeCompletionCallback ); } + if ( getSessionFactoryOptions().isQueryCacheEnabled() ) { invalidateSpaces( executable.getPropertySpaces() ); } - if ( executable.getAfterTransactionCompletionProcess() != null ) { - if ( afterTransactionProcesses == null ) { - afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); - } - afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() ); + + final AfterTransactionCompletionProcess afterCompletionCallback = executable.getAfterTransactionCompletionProcess(); + if ( afterCompletionCallback != null ) { + transactionCompletionCallbacks.registerCallback( afterCompletionCallback ); } } @@ -460,18 +456,14 @@ public void checkNoUnresolvedActionsAfterOperation() throws PropertyValueExcepti } } - public void registerProcess(AfterTransactionCompletionProcess process) { - if ( afterTransactionProcesses == null ) { - afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); - } - afterTransactionProcesses.register( process ); + @Override + public void registerCallback(BeforeCompletionCallback process) { + transactionCompletionCallbacks.registerCallback( process ); } - public void registerProcess(BeforeTransactionCompletionProcess process) { - if ( beforeTransactionProcesses == null ) { - beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); - } - beforeTransactionProcesses.register( process ); + @Override + public void registerCallback(AfterCompletionCallback process) { + transactionCompletionCallbacks.registerCallback( process ); } /** @@ -542,9 +534,7 @@ private void prepareActions(@Nullable ExecutableList queue) throws HibernateE public void afterTransactionCompletion(boolean success) { if ( !isTransactionCoordinatorShared ) { // Execute completion actions only in transaction owner (aka parent session). - if ( afterTransactionProcesses != null ) { - afterTransactionProcesses.afterTransactionCompletion( success ); - } + transactionCompletionCallbacks.afterTransactionCompletion( success ); } } @@ -554,9 +544,7 @@ public void afterTransactionCompletion(boolean success) { public void beforeTransactionCompletion() { if ( !isTransactionCoordinatorShared ) { // Execute completion actions only in transaction owner (aka parent session). - if ( beforeTransactionProcesses != null ) { - beforeTransactionProcesses.beforeTransactionCompletion(); - } + transactionCompletionCallbacks.beforeTransactionCompletion(); // Make sure to always execute pending batches before the transaction completes. // One such pending batch could be the pessimistic version increment for an entity session.getJdbcCoordinator().executeBatch(); @@ -646,17 +634,14 @@ private void executeActions(@Nullable Executabl executable.execute(); } finally { - if ( executable.getBeforeTransactionCompletionProcess() != null ) { - if ( beforeTransactionProcesses == null ) { - beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); - } - beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() ); + final BeforeTransactionCompletionProcess beforeCompletionProcess = executable.getBeforeTransactionCompletionProcess(); + if ( beforeCompletionProcess != null ) { + transactionCompletionCallbacks.registerCallback( beforeCompletionProcess ); } - if ( executable.getAfterTransactionCompletionProcess() != null ) { - if ( afterTransactionProcesses == null ) { - afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); - } - afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() ); + + final AfterTransactionCompletionProcess afterCompletionProcess = executable.getAfterTransactionCompletionProcess(); + if ( afterCompletionProcess != null ) { + transactionCompletionCallbacks.registerCallback( afterCompletionProcess ); } } } @@ -696,10 +681,7 @@ public > void execute(E executable) { private void invalidateSpaces(String @Nullable [] spaces) { if ( spaces != null && spaces.length > 0 ) { for ( String space : spaces ) { - if ( afterTransactionProcesses == null ) { - afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); - } - afterTransactionProcesses.addSpaceToInvalidate( space ); + transactionCompletionCallbacks.addSpaceToInvalidate( space ); } // Performance win: If we are processing an ExecutableList, this will only be called once session.getFactory().getCache().getTimestampsCache().preInvalidate( spaces, session ); @@ -755,14 +737,8 @@ public int numberOfInsertions() { return insertions == null ? 0 : insertions.size(); } - public TransactionCompletionProcesses getTransactionCompletionProcesses() { - if ( beforeTransactionProcesses == null ) { - beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); - } - if ( afterTransactionProcesses == null ) { - afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); - } - return new TransactionCompletionProcesses( beforeTransactionProcesses, afterTransactionProcesses ); + public TransactionCompletionCallbacksImpl getTransactionCompletionCallbacks() { + return transactionCompletionCallbacks.forSharing(); } /** @@ -770,15 +746,14 @@ public TransactionCompletionProcesses getTransactionCompletionProcesses() { * Transaction completion processes are always executed by transaction owner (primary session), * but can be registered using secondary session too. * - * @param processes Transaction completion processes. + * @param callbacks Transaction completion callbacks. * @param isTransactionCoordinatorShared Flag indicating shared transaction context. */ - public void setTransactionCompletionProcesses( - TransactionCompletionProcesses processes, + public void setTransactionCompletionCallbacks( + TransactionCompletionCallbacksImpl callbacks, boolean isTransactionCoordinatorShared) { this.isTransactionCoordinatorShared = isTransactionCoordinatorShared; - this.beforeTransactionProcesses = processes.beforeTransactionCompletionProcesses; - this.afterTransactionProcesses = processes.afterTransactionCompletionProcesses; + this.transactionCompletionCallbacks = callbacks; } public void sortCollectionActions() { @@ -843,14 +818,12 @@ public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) { public boolean hasAfterTransactionActions() { return !isTransactionCoordinatorShared - && afterTransactionProcesses != null - && afterTransactionProcesses.hasActions(); + && transactionCompletionCallbacks.hasAfterCompletionCallbacks(); } public boolean hasBeforeTransactionActions() { return !isTransactionCoordinatorShared - && beforeTransactionProcesses != null - && beforeTransactionProcesses.hasActions(); + && transactionCompletionCallbacks.hasBeforeCompletionCallbacks(); } public boolean hasAnyQueuedActions() { @@ -980,107 +953,6 @@ public static ActionQueue deserialize(ObjectInputStream ois, EventSource session return rtn; } - private abstract static class AbstractTransactionCompletionProcessQueue { - protected SessionImplementor session; - // Concurrency handling required when transaction completion process is dynamically registered - // inside event listener (HHH-7478). - protected ConcurrentLinkedQueue<@NonNull T> processes = new ConcurrentLinkedQueue<>(); - - private AbstractTransactionCompletionProcessQueue(SessionImplementor session) { - this.session = session; - } - - public void register(@Nullable T process) { - if ( process != null ) { - processes.add( process ); - } - } - - public boolean hasActions() { - return !processes.isEmpty(); - } - } - - /** - * Encapsulates behavior needed for before transaction processing - */ - private static class BeforeTransactionCompletionProcessQueue - extends AbstractTransactionCompletionProcessQueue { - - private BeforeTransactionCompletionProcessQueue(SessionImplementor session) { - super( session ); - } - - public void beforeTransactionCompletion() { - BeforeTransactionCompletionProcess process; - while ( ( process = processes.poll() ) != null ) { - try { - process.doBeforeTransactionCompletion( session ); - } - catch (HibernateException he) { - throw he; - } - catch (Exception e) { - throw new HibernateException( "Unable to perform beforeTransactionCompletion callback: " + e.getMessage(), e ); - } - } - } - } - - /** - * Encapsulates behavior needed for after transaction processing - */ - private static class AfterTransactionCompletionProcessQueue - extends AbstractTransactionCompletionProcessQueue { - private final Set querySpacesToInvalidate = new HashSet<>(); - - private AfterTransactionCompletionProcessQueue(SessionImplementor session) { - super( session ); - } - - public void addSpaceToInvalidate(String space) { - querySpacesToInvalidate.add( space ); - } - - public void afterTransactionCompletion(boolean success) { - AfterTransactionCompletionProcess process; - while ( ( process = processes.poll() ) != null ) { - try { - process.doAfterTransactionCompletion( success, session ); - } - catch (CacheException ce) { - LOG.unableToReleaseCacheLock( ce ); - // continue loop - } - catch (Exception e) { - throw new HibernateException( "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e ); - } - } - - final SessionFactoryImplementor factory = session.getFactory(); - if ( factory.getSessionFactoryOptions().isQueryCacheEnabled() ) { - factory.getCache().getTimestampsCache() - .invalidate( querySpacesToInvalidate.toArray(new String[0]), session ); - } - querySpacesToInvalidate.clear(); - } - } - - /** - * Wrapper class allowing to bind the same transaction completion process queues in different sessions. - */ - public static class TransactionCompletionProcesses { - private final BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcesses; - private final AfterTransactionCompletionProcessQueue afterTransactionCompletionProcesses; - - private TransactionCompletionProcesses( - BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcessQueue, - AfterTransactionCompletionProcessQueue afterTransactionCompletionProcessQueue) { - this.beforeTransactionCompletionProcesses = beforeTransactionCompletionProcessQueue; - this.afterTransactionCompletionProcesses = afterTransactionCompletionProcessQueue; - } - } - /** * Order the {@link #insertions} queue such that we group inserts against the same entity together (without * violating constraints). The original order is generated by cascade order, which in turn is based on the diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java index cd495ccde8c5..804cb47f7288 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java @@ -19,7 +19,7 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.FilterImpl; -import org.hibernate.internal.SessionCreationOptions; +import org.hibernate.engine.creation.internal.SessionCreationOptions; import org.hibernate.loader.ast.spi.CascadingFetchProfile; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionBuilderImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionBuilderImplementor.java deleted file mode 100644 index 049d73d15cdb..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionBuilderImplementor.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.engine.spi; - -import org.hibernate.SessionBuilder; - -/** - * Defines the internal contract between the {@link SessionBuilder} and - * other parts of Hibernate. - * - * @see SessionBuilder - * - * @author Gail Badner - */ -public interface SessionBuilderImplementor extends SessionBuilder { -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index 1a3e6f483fe9..ece114ba9ab9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -36,10 +36,10 @@ import org.hibernate.ReplicationMode; import org.hibernate.SessionEventListener; import org.hibernate.SharedSessionBuilder; +import org.hibernate.SharedStatelessSessionBuilder; import org.hibernate.SimpleNaturalIdLoadAccess; import org.hibernate.Transaction; import org.hibernate.UnknownProfileException; -import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers; import org.hibernate.cache.spi.CacheTransactionSynchronization; import org.hibernate.collection.spi.PersistentCollection; @@ -111,6 +111,11 @@ public T execute(Callback callback) { return delegate.execute( callback ); } + @Override + public SharedStatelessSessionBuilder statelessWithOptions() { + return delegate.statelessWithOptions(); + } + @Override public String getTenantIdentifier() { return delegate.getTenantIdentifier(); @@ -1156,8 +1161,8 @@ public ActionQueue getActionQueue() { } @Override - public void registerProcess(AfterTransactionCompletionProcess process) { - delegate.registerProcess( process ); + public TransactionCompletionCallbacks getTransactionCompletionCallbacks() { + return delegate.getTransactionCompletionCallbacks(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java index 274015be5b4e..0a39869ab02c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java @@ -34,6 +34,7 @@ import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.spi.CacheImplementor; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.profile.FetchProfile; import org.hibernate.event.service.spi.EventListenerRegistry; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index 87af19367ca2..1f5625e7123d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -17,6 +17,7 @@ import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.spi.CacheImplementor; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.profile.FetchProfile; import org.hibernate.event.service.spi.EventListenerRegistry; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionImplementor.java index bf62391fefe9..daefd65cff10 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionImplementor.java @@ -73,6 +73,11 @@ default SessionImplementor getSession() { */ ActionQueue getActionQueue(); + @Override + default TransactionCompletionCallbacks getTransactionCompletionCallbacks() { + return getActionQueue(); + } + @Override Object instantiate(EntityPersister persister, Object id) throws HibernateException; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java index b8b0195dd657..13bfb10a7ee7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java @@ -40,6 +40,7 @@ import org.hibernate.SessionEventListener; import org.hibernate.SessionFactory; import org.hibernate.SharedSessionBuilder; +import org.hibernate.SharedStatelessSessionBuilder; import org.hibernate.SimpleNaturalIdLoadAccess; import org.hibernate.Transaction; import org.hibernate.UnknownProfileException; @@ -544,6 +545,11 @@ public Query createQuery(CriteriaUpdate updateQuery) { return this.lazySession.get().createQuery( updateQuery ); } + @Override + public SharedStatelessSessionBuilder statelessWithOptions() { + return this.lazySession.get().statelessWithOptions(); + } + @Override public String getTenantIdentifier() { return this.lazySession.get().getTenantIdentifier(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java index 2cc3517198a5..715f13f700e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java @@ -16,7 +16,6 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.StatelessSession; -import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers; import org.hibernate.dialect.Dialect; import org.hibernate.event.spi.EventSource; @@ -247,6 +246,14 @@ default void checkTransactionNeededForUpdateOperation(String exceptionMessage) { */ Transaction accessTransaction(); + /** + * Access to register callbacks for transaction completion processing. + * + * @since 7.2 + */ + @Incubating + TransactionCompletionCallbacks getTransactionCompletionCallbacks(); + /** * Instantiate an {@link EntityKey} with the given id and for the * entity represented by the given {@link EntityPersister}. @@ -409,6 +416,16 @@ default EventSource asEventSource() { throw new ClassCastException( "session is not an EventSource" ); } + /** + * Whether the session {@linkplain StatelessSessionImplementor stateless}, as opposed tp + * {@linkplain SessionImplementor stateful}. + * + * @apiNote Essentially, whether casting this session to {@linkplain StatelessSessionImplementor} will succeed. + */ + default boolean isStateless() { + return false; + } + /** * Called after each operation on a {@link org.hibernate.ScrollableResults}, * providing an opportunity for a stateless session to clear its @@ -551,15 +568,6 @@ default boolean isStatelessSession() { */ void lock(String entityName, Object child, LockOptions lockOptions); - /** - * Registers the given process for execution after transaction completion. - * - * @param process The process to register - * @since 7.0 - */ - @Incubating - void registerProcess(AfterTransactionCompletionProcess process); - /** * Attempts to load the entity from the second-level cache. * diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java index 5175ea06e686..e8ececa07237 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java @@ -18,8 +18,8 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.SharedSessionContract; +import org.hibernate.SharedStatelessSessionBuilder; import org.hibernate.Transaction; -import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers; import org.hibernate.cache.spi.CacheTransactionSynchronization; import org.hibernate.collection.spi.PersistentCollection; @@ -75,6 +75,11 @@ protected SharedSessionContract delegate() { return delegate; } + @Override + public SharedStatelessSessionBuilder statelessWithOptions() { + return delegate.statelessWithOptions(); + } + @Override public String getTenantIdentifier() { return delegate.getTenantIdentifier(); @@ -411,6 +416,11 @@ public Transaction accessTransaction() { return delegate.accessTransaction(); } + @Override + public TransactionCompletionCallbacks getTransactionCompletionCallbacks() { + return delegate.getTransactionCompletionCallbacks(); + } + @Override public EntityKey generateEntityKey(Object id, EntityPersister persister) { return delegate.generateEntityKey( id, persister ); @@ -687,11 +697,6 @@ public void lock(String entityName, Object child, LockOptions lockOptions) { delegate.lock( entityName, child, lockOptions ); } - @Override - public void registerProcess(AfterTransactionCompletionProcess process) { - delegate.registerProcess( process ); - } - @Override public Object loadFromSecondLevelCache(EntityPersister persister, EntityKey entityKey, Object instanceToLoad, LockMode lockMode) { return delegate.loadFromSecondLevelCache( persister, entityKey, instanceToLoad, lockMode ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/StatelessSessionImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/StatelessSessionImplementor.java new file mode 100644 index 000000000000..a5840d6d5921 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/StatelessSessionImplementor.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.spi; + +import org.hibernate.StatelessSession; + +/** + * SPI extension of StatelessSession + * + * @author Steve Ebersole + */ +public interface StatelessSessionImplementor extends StatelessSession, SharedSessionContractImplementor { + @Override + default boolean isStateless() { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/TransactionCompletionCallbacks.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/TransactionCompletionCallbacks.java new file mode 100644 index 000000000000..53e6401c12ad --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/TransactionCompletionCallbacks.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.spi; + +import org.hibernate.Incubating; + +/** + * Collection of {@linkplain BeforeCompletionCallback before} and {@linkplain AfterCompletionCallback after} + * callbacks related to transaction completion. + * + * @author Steve Ebersole + */ +@Incubating +public interface TransactionCompletionCallbacks { + /** + * Commonality for {@linkplain BeforeCompletionCallback before} and + * {@linkplain AfterCompletionCallback after} callbacks. + */ + interface CompletionCallback { + } + + interface BeforeCompletionCallback extends CompletionCallback { + /** + * Perform whatever processing is encapsulated here before completion of the transaction. + * + * @param session The session on which the transaction is preparing to complete. + */ + void doBeforeTransactionCompletion(SharedSessionContractImplementor session); + + } + + interface AfterCompletionCallback extends CompletionCallback { + /** + * Perform whatever processing is encapsulated here after completion of the transaction. + * + * @param success Did the transaction complete successfully? True means it did. + * @param session The session on which the transaction is completing. + */ + void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session); + } + + /** + * Register a {@code process} (callback) to be performed at the start of transaction completion. + * + * @param process The callback. + */ + void registerCallback(BeforeCompletionCallback process); + + /** + * Register a {@code process} (callback) to be performed at the end of transaction completion. + * + * @param process The callback. + */ + void registerCallback(AfterCompletionCallback process); +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java index 259a1290ec62..c4f91b9c225f 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java @@ -13,7 +13,7 @@ import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.Status; -import org.hibernate.event.spi.AbstractEvent; +import org.hibernate.event.spi.AbstractSessionEvent; import org.hibernate.event.spi.LockEvent; import org.hibernate.event.spi.LockEventListener; import org.hibernate.internal.CoreMessageLogger; @@ -113,7 +113,7 @@ private void cascadeOnLock(LockEvent event, EntityPersister persister, Object en * * @return An EntityEntry representing the entity within this session. */ - protected final EntityEntry reassociate(AbstractEvent event, Object object, Object id, EntityPersister persister) { + protected final EntityEntry reassociate(AbstractSessionEvent event, Object object, Object id, EntityPersister persister) { if ( LOG.isTraceEnabled() ) { LOG.trace( "Reassociating transient instance: " + infoString( persister, id, event.getFactory() ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java index e7130c4e8aca..a929a2362b94 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java @@ -51,10 +51,10 @@ public void onPostLoad(PostLoadEvent event) { OptimisticLockHelper.forceVersionIncrement( entity, entry, session ); break; case OPTIMISTIC_FORCE_INCREMENT: - session.getActionQueue().registerProcess( new EntityIncrementVersionProcess( entity ) ); + session.getActionQueue().registerCallback( new EntityIncrementVersionProcess( entity ) ); break; case OPTIMISTIC: - session.getActionQueue().registerProcess( new EntityVerifyVersionProcess( entity ) ); + session.getActionQueue().registerCallback( new EntityVerifyVersionProcess( entity ) ); break; } } 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 797ed7220049..80bb56daec38 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 @@ -233,7 +233,7 @@ private static void evictEntity(Object object, EntityPersister persister, Object ); final SoftLock lock = cache.lockItem( source, ck, previousVersion ); cache.remove( source, ck ); - source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); + source.getActionQueue().registerCallback( (success, session) -> cache.unlockItem( session, ck, lock ) ); } } @@ -342,7 +342,7 @@ private static void evictCachedCollections(Type[] types, Object id, EventSource ); final SoftLock lock = cache.lockItem( source, ck, null ); cache.remove( source, ck ); - actionQueue.registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); + actionQueue.registerCallback( (success, session) -> cache.unlockItem( session, ck, lock ) ); } } else if ( type instanceof ComponentType compositeType ) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/PostInsertEventListenerStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/event/internal/PostInsertEventListenerStandardImpl.java index fe9ad65ad96c..224bc5866e4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/PostInsertEventListenerStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/PostInsertEventListenerStandardImpl.java @@ -18,6 +18,9 @@ public class PostInsertEventListenerStandardImpl implements PostInsertEventListener, CallbackRegistryConsumer { private CallbackRegistry callbackRegistry; + public PostInsertEventListenerStandardImpl() { + } + @Override public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { this.callbackRegistry = callbackRegistry; diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpdateEventListenerStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpdateEventListenerStandardImpl.java index dd0d2bbbc233..49792534e893 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpdateEventListenerStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpdateEventListenerStandardImpl.java @@ -4,8 +4,8 @@ */ package org.hibernate.event.internal; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; import org.hibernate.jpa.event.spi.CallbackRegistry; @@ -29,9 +29,9 @@ public void onPostUpdate(PostUpdateEvent event) { handlePostUpdate( event.getEntity(), event.getSession() ); } - private void handlePostUpdate(Object entity, EventSource source) { + private void handlePostUpdate(Object entity, SharedSessionContractImplementor source) { // mimic the preUpdate filter - if ( source == null // it must be a StatelessSession + if ( source.isStateless() || source.getPersistenceContextInternal().getEntry(entity).getStatus() != Status.DELETED ) { callbackRegistry.postUpdate(entity); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpsertEventListenerStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpsertEventListenerStandardImpl.java index 390abf11cf4c..3cbd7abc909a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpsertEventListenerStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/PostUpsertEventListenerStandardImpl.java @@ -4,7 +4,7 @@ */ package org.hibernate.event.internal; -import org.hibernate.event.spi.EventSource; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.PostUpsertEvent; import org.hibernate.event.spi.PostUpsertEventListener; import org.hibernate.jpa.event.spi.CallbackRegistry; @@ -29,7 +29,7 @@ public void onPostUpsert(PostUpsertEvent event) { handlePostUpsert( event.getEntity(), event.getSession() ); } - private void handlePostUpsert(Object entity, EventSource source) { + private void handlePostUpsert(Object entity, SharedSessionContractImplementor source) { // // mimic the preUpdate filter // if ( source == null // it must be a StatelessSession // || source.getPersistenceContextInternal().getEntry(entity).getStatus() != Status.DELETED ) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/package-info.java b/hibernate-core/src/main/java/org/hibernate/event/package-info.java index 3ce6afb2502b..c237ff6009cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/event/package-info.java @@ -6,7 +6,7 @@ /** * This package defines a framework which models events occurring * within a stateful Hibernate {@link org.hibernate.Session}. An - * {@linkplain org.hibernate.event.spi.AbstractEvent event} + * {@linkplain org.hibernate.event.spi.AbstractSessionEvent event} * represents a request by the session API for some work to be * performed, and an event listener must respond to the event and * do that work, usually by scheduling some sort of diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractCollectionEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractCollectionEvent.java index 33e93d63e55f..9ffbe16cd706 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractCollectionEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractCollectionEvent.java @@ -12,7 +12,7 @@ * * @author Gail Badner */ -public abstract class AbstractCollectionEvent extends AbstractEvent { +public abstract class AbstractCollectionEvent extends AbstractSessionEvent { private final PersistentCollection collection; private final Object affectedOwner; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractDatabaseOperationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractDatabaseOperationEvent.java index 8bcb94f39904..2f88ea46e12b 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractDatabaseOperationEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractDatabaseOperationEvent.java @@ -4,19 +4,20 @@ */ package org.hibernate.event.spi; -import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** - * Abstract supertype of {@link AbstractPostDatabaseOperationEvent} - * and {@link AbstractPostDatabaseOperationEvent}. + * Base for events which denote database operations. + * + * @see AbstractPreDatabaseOperationEvent + * @see AbstractPostDatabaseOperationEvent * * @author Gavin King * * @since 7 */ public abstract class AbstractDatabaseOperationEvent extends AbstractEvent { - private final Object entity; private final Object id; private final EntityPersister persister; @@ -30,7 +31,7 @@ public abstract class AbstractDatabaseOperationEvent extends AbstractEvent { * @param persister The entity's persister. */ public AbstractDatabaseOperationEvent( - EventSource source, + SharedSessionContractImplementor source, Object entity, Object id, EntityPersister persister) { @@ -42,8 +43,6 @@ public AbstractDatabaseOperationEvent( /** * Retrieves the entity involved in the database operation. - * - * @return The entity. */ public Object getEntity() { return entity; @@ -51,8 +50,6 @@ public Object getEntity() { /** * The id to be used in the database operation. - * - * @return The id. */ public Object getId() { return id; @@ -60,20 +57,8 @@ public Object getId() { /** * The persister for the entity. - * - * @return The entity persister. */ public EntityPersister getPersister() { return persister; } - - /** - * The factory which owns the persister for the entity. - * - * @return The factory - */ - @Override - public SessionFactoryImplementor getFactory() { - return persister.getFactory(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractEvent.java index 946cf88fd40e..c5235acb77fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractEvent.java @@ -5,38 +5,28 @@ package org.hibernate.event.spi; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import java.io.Serializable; /** - * Defines a base class for {@link org.hibernate.Session}-generated events. + * Base class for events which are generated from a {@link org.hibernate.Session} + * or {@linkplain org.hibernate.StatelessSession}. * * @author Steve Ebersole */ public abstract class AbstractEvent implements Serializable { + protected final SharedSessionContractImplementor source; - private final EventSource session; - - /** - * Constructs an event from the given event session. - * - * @param source The session event source. - */ - public AbstractEvent(EventSource source) { - this.session = source; + public AbstractEvent(SharedSessionContractImplementor source) { + this.source = source; } - /** - * Returns the session event source for this event. This is the underlying - * session from which this event was generated. - * - * @return The session event source. - */ - public final EventSource getSession() { - return session; + public SharedSessionContractImplementor getSession() { + return source; } public SessionFactoryImplementor getFactory() { - return session.getFactory(); + return source.getFactory(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPostDatabaseOperationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPostDatabaseOperationEvent.java index 5f491566424f..83be95da9c55 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPostDatabaseOperationEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPostDatabaseOperationEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -24,7 +25,7 @@ public abstract class AbstractPostDatabaseOperationEvent extends AbstractDatabas * @param persister The entity's persister. */ public AbstractPostDatabaseOperationEvent( - EventSource source, + SharedSessionContractImplementor source, Object entity, Object id, EntityPersister persister) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java index 5f9b8054d468..24577a5fb764 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -22,7 +23,7 @@ public abstract class AbstractPreDatabaseOperationEvent extends AbstractDatabase * @param persister The entity's persister. */ public AbstractPreDatabaseOperationEvent( - EventSource source, + SharedSessionContractImplementor source, Object entity, Object id, EntityPersister persister) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractSessionEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractSessionEvent.java new file mode 100644 index 000000000000..2a826ec7a176 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractSessionEvent.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.event.spi; + +import org.hibernate.engine.spi.SessionFactoryImplementor; + +import java.io.Serializable; + +/** + * Base class for events which are generated from a {@linkplain org.hibernate.Session Session} + * ({@linkplain EventSource}). + * + * @author Steve Ebersole + */ +public abstract class AbstractSessionEvent implements Serializable { + protected final EventSource source; + + /** + * Constructs an event from the given event session. + * + * @param source The session event source. + */ + public AbstractSessionEvent(EventSource source) { + this.source = source; + } + + /** + * Returns the session event source for this event. This is the underlying + * session from which this event was generated. + * + * @return The session event source. + */ + public final EventSource getSession() { + return getEventSource(); + } + + public final EventSource getEventSource() { + return source.asEventSource(); + } + + public SessionFactoryImplementor getFactory() { + return source.getFactory(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/ClearEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/ClearEvent.java index 8f11899dad90..b10e916fbf95 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/ClearEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/ClearEvent.java @@ -11,7 +11,7 @@ * * @see org.hibernate.Session#clear */ -public class ClearEvent extends AbstractEvent { +public class ClearEvent extends AbstractSessionEvent { public ClearEvent(EventSource source) { super( source ); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/DeleteEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/DeleteEvent.java index d6c8e786d48a..79131bfed46a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/DeleteEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/DeleteEvent.java @@ -14,7 +14,7 @@ * * @see org.hibernate.Session#remove */ -public class DeleteEvent extends AbstractEvent { +public class DeleteEvent extends AbstractSessionEvent { private final Object object; private String entityName; private boolean cascadeDeleteEnabled; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/DirtyCheckEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/DirtyCheckEvent.java index 43f3bf9cb343..0e00edc50ab2 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/DirtyCheckEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/DirtyCheckEvent.java @@ -12,7 +12,7 @@ * * @see org.hibernate.Session#isDirty */ -public class DirtyCheckEvent extends AbstractEvent { +public class DirtyCheckEvent extends AbstractSessionEvent { private boolean dirty; public DirtyCheckEvent(EventSource source) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EvictEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EvictEvent.java index a30c375f33fd..2e221294369d 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/EvictEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EvictEvent.java @@ -13,7 +13,7 @@ * @see org.hibernate.Session#evict * @see org.hibernate.Session#detach */ -public class EvictEvent extends AbstractEvent { +public class EvictEvent extends AbstractSessionEvent { private Object object; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEntityEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEntityEvent.java index 4668e0a68222..962821b38d94 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEntityEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEntityEvent.java @@ -9,7 +9,7 @@ /** * @author Gavin King */ -public class FlushEntityEvent extends AbstractEvent { +public class FlushEntityEvent extends AbstractSessionEvent { private Object entity; private Object[] propertyValues; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEvent.java index bfcbb9be0558..76405ff71a33 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/FlushEvent.java @@ -11,7 +11,7 @@ * * @see org.hibernate.Session#flush */ -public class FlushEvent extends AbstractEvent { +public class FlushEvent extends AbstractSessionEvent { private int numberOfEntitiesProcessed; private int numberOfCollectionsProcessed; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java index b7fb598e141e..e85ba703c64a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java @@ -13,7 +13,7 @@ * * @author Steve Ebersole */ -public class LoadEvent extends AbstractEvent { +public class LoadEvent extends AbstractSessionEvent { private Object entityId; private String entityClassName; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/LockEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/LockEvent.java index 00cd6211e09a..82c59955c8e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/LockEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/LockEvent.java @@ -16,7 +16,7 @@ * * @see org.hibernate.Session#lock */ -public class LockEvent extends AbstractEvent { +public class LockEvent extends AbstractSessionEvent { public static final String ILLEGAL_SKIP_LOCKED = "Skip-locked is not valid option for #lock"; private Object object; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/MergeEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/MergeEvent.java index b4b533335e57..85842dba2aa9 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/MergeEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/MergeEvent.java @@ -11,7 +11,7 @@ * * @see org.hibernate.Session#merge */ -public class MergeEvent extends AbstractEvent { +public class MergeEvent extends AbstractSessionEvent { private Object original; private Object requestedId; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PersistEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PersistEvent.java index f7ca2c43e588..529ec492cda9 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PersistEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PersistEvent.java @@ -11,7 +11,7 @@ * * @see org.hibernate.Session#persist */ -public class PersistEvent extends AbstractEvent { +public class PersistEvent extends AbstractSessionEvent { private Object object; private String entityName; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEvent.java index b254f048013a..adc9a0ddd3c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -19,7 +20,7 @@ public PostDeleteEvent( Object id, Object[] deletedState, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.deletedState = deletedState; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEvent.java index e6315c1e5894..8515f2afebf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -19,7 +20,7 @@ public PostInsertEvent( Object id, Object[] state, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostLoadEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostLoadEvent.java index 5c8f162352a9..688302e5b4f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostLoadEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostLoadEvent.java @@ -11,7 +11,7 @@ * * @author Kabir Khan, Gavin King */ -public class PostLoadEvent extends AbstractEvent { +public class PostLoadEvent extends AbstractSessionEvent { private Object entity; private Object id; private EntityPersister persister; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEvent.java index 78783bf134b5..2d5c36b1ea47 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -24,7 +25,7 @@ public PostUpdateEvent( Object[] oldState, int[] dirtyProperties, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; this.oldState = oldState; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpsertEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpsertEvent.java index e7e39991963c..665356d50a6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpsertEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpsertEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -22,7 +23,7 @@ public PostUpsertEvent( Object[] state, int[] dirtyProperties, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; this.dirtyProperties = dirtyProperties; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java index a8ebbdd1e881..1f2ede9cc784 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; @@ -31,7 +32,7 @@ public PreDeleteEvent( Object id, Object[] deletedState, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.deletedState = deletedState; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreInsertEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreInsertEvent.java index 9c565f87d8da..b98887cce653 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreInsertEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreInsertEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -29,7 +30,7 @@ public PreInsertEvent( Object id, Object[] state, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java index dbf45d920591..2c5e0a538d4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java @@ -11,7 +11,7 @@ * * @author Gavin King */ -public class PreLoadEvent extends AbstractEvent { +public class PreLoadEvent extends AbstractSessionEvent { private Object entity; private Object[] state; private Object id; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpdateEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpdateEvent.java index dbfb1ef9fa43..ee1ced216738 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpdateEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpdateEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -33,7 +34,7 @@ public PreUpdateEvent( Object[] state, Object[] oldState, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; this.oldState = oldState; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpsertEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpsertEvent.java index 02e04e80287d..34881b88adb1 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpsertEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreUpsertEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; /** @@ -28,7 +29,7 @@ public PreUpsertEvent( Object id, Object[] state, EntityPersister persister, - EventSource source) { + SharedSessionContractImplementor source) { super( source, entity, id, persister ); this.state = state; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshEvent.java index f542966754e5..94ce4fd22e99 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/RefreshEvent.java @@ -16,7 +16,7 @@ * * @see org.hibernate.Session#refresh */ -public class RefreshEvent extends AbstractEvent { +public class RefreshEvent extends AbstractSessionEvent { private final Object object; private String entityName; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/ReplicateEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/ReplicateEvent.java index 8e6130a7852a..b3ff5bcccd79 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/ReplicateEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/ReplicateEvent.java @@ -13,7 +13,7 @@ * * @see org.hibernate.Session#replicate */ -public class ReplicateEvent extends AbstractEvent { +public class ReplicateEvent extends AbstractSessionEvent { private Object object; private ReplicationMode replicationMode; private String entityName; diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/event/spi/package-info.java index 4ab31f4aa15c..fa68fbf1d6e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/package-info.java @@ -7,7 +7,7 @@ * Defines the event types and event listener interfaces for * events produced by the stateful {@link org.hibernate.Session}. *

- * An {@linkplain org.hibernate.event.spi.AbstractEvent event} + * An {@linkplain org.hibernate.event.spi.AbstractSessionEvent event} * represents a request by the session API for some work to be * performed, and an event listener must respond to the event and * do that work, usually by scheduling some sort of 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 ac6a92a7cfa6..18f743a85352 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -20,6 +20,7 @@ import org.hibernate.LockMode; import org.hibernate.SessionEventListener; import org.hibernate.SessionException; +import org.hibernate.SharedStatelessSessionBuilder; import org.hibernate.Transaction; import org.hibernate.UnknownEntityTypeException; import org.hibernate.binder.internal.TenantIdBinder; @@ -27,6 +28,9 @@ import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers; import org.hibernate.cache.spi.CacheTransactionSynchronization; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.creation.internal.SessionCreationOptions; +import org.hibernate.engine.creation.internal.SharedSessionCreationOptions; +import org.hibernate.engine.creation.internal.SharedStatelessSessionBuilderImpl; import org.hibernate.engine.internal.SessionEventListenerManagerImpl; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; @@ -232,6 +236,11 @@ final SessionFactoryOptions getSessionFactoryOptions() { return factoryOptions; } + @Override + public SharedStatelessSessionBuilder statelessWithOptions() { + return new SharedStatelessSessionBuilderImpl( this ); + } + private static boolean isTransactionCoordinatorShared(SessionCreationOptions options) { return options instanceof SharedSessionCreationOptions sharedSessionCreationOptions && sharedSessionCreationOptions.isTransactionCoordinatorShared(); @@ -676,7 +685,7 @@ protected void delayedAfterCompletion() { } } - protected Transaction getCurrentTransaction() { + public Transaction getCurrentTransaction() { return currentHibernateTransaction; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CommonSharedSessionCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/internal/CommonSharedSessionCreationOptions.java new file mode 100644 index 000000000000..db405a783090 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/CommonSharedSessionCreationOptions.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal; + +import org.hibernate.CacheMode; +import org.hibernate.Interceptor; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; +import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.resource.transaction.spi.TransactionCoordinator; + +/** + * Creation options for shared {@linkplain org.hibernate.engine.spi.SessionImplementor stateful} + * and {@linkplain org.hibernate.engine.spi.StatelessSessionImplementor stateless} sessions. + * + * @implNote At the moment this is only used in the creation of {@linkplain org.hibernate.engine.spi.StatelessSessionImplementor stateless} sessions. + * + * @author Steve Ebersole + */ +public interface CommonSharedSessionCreationOptions { + + Interceptor getInterceptor(); + + StatementInspector getStatementInspector(); + + Object getTenantIdentifierValue(); + + boolean isReadOnly(); + + CacheMode getInitialCacheMode(); + + boolean isTransactionCoordinatorShared(); + TransactionCoordinator getTransactionCoordinator(); + JdbcCoordinator getJdbcCoordinator(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java index 0f978e279b08..fc3e459db815 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/OptimisticLockHelper.java @@ -53,7 +53,7 @@ public static void forceVersionIncrement(Object object, EntityEntry entry, Share persister, session ); - session.registerProcess( new CacheCleanupProcess( + session.getTransactionCompletionCallbacks().registerCallback( new CacheCleanupProcess( cacheKey, persister, previousVersion, 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 697d9cf1fa08..a4a818008ca1 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -17,26 +17,19 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.UnaryOperator; import javax.naming.Reference; import javax.naming.StringRefAddr; import jakarta.persistence.TypedQuery; -import org.hibernate.CacheMode; -import org.hibernate.ConnectionAcquisitionMode; -import org.hibernate.ConnectionReleaseMode; import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.EntityNameResolver; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.Session; -import org.hibernate.SessionBuilder; -import org.hibernate.SessionEventListener; import org.hibernate.SessionFactory; import org.hibernate.SessionFactoryObserver; import org.hibernate.StatelessSession; @@ -59,13 +52,15 @@ import org.hibernate.context.spi.CurrentSessionContext; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.creation.internal.SessionBuilderImpl; +import org.hibernate.engine.creation.internal.StatelessSessionBuilderImpl; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; import org.hibernate.engine.jdbc.batch.spi.BatchBuilder; 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.engine.profile.FetchProfile; import org.hibernate.engine.spi.FilterDefinition; -import org.hibernate.engine.spi.SessionBuilderImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; @@ -81,7 +76,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.IntegratorService; -import org.hibernate.jpa.internal.ExceptionMapperLegacyJpaImpl; import org.hibernate.jpa.internal.PersistenceUnitUtilImpl; import org.hibernate.mapping.GeneratorSettings; import org.hibernate.mapping.RootClass; @@ -105,9 +99,6 @@ import org.hibernate.relational.SchemaManager; import org.hibernate.relational.internal.SchemaManagerImpl; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; -import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; -import org.hibernate.resource.jdbc.spi.StatementInspector; -import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ExceptionMapper; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -120,8 +111,6 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; -import org.jboss.logging.Logger; - import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceException; @@ -132,7 +121,6 @@ import jakarta.persistence.TypedQueryReference; import static jakarta.persistence.SynchronizationType.SYNCHRONIZED; -import static java.util.Collections.addAll; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static org.hibernate.cfg.AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS; @@ -199,8 +187,8 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { private final transient EventListenerGroups eventListenerGroups; private final transient WrapperOptions wrapperOptions; - private final transient SessionBuilderImpl defaultSessionOpenOptions; - private final transient SessionBuilderImpl temporarySessionOpenOptions; + private final transient SessionBuilderImplementor defaultSessionOpenOptions; + private final transient SessionBuilderImplementor temporarySessionOpenOptions; private final transient StatelessSessionBuilder defaultStatelessOptions; private final transient EntityNameResolver entityNameResolver; @@ -456,13 +444,13 @@ private void disintegrate(Exception startupException, IntegratorObserver integra } - private SessionBuilderImpl createDefaultSessionOpenOptionsIfPossible() { + private SessionBuilderImplementor createDefaultSessionOpenOptionsIfPossible() { final var tenantIdResolver = getCurrentTenantIdentifierResolver(); // Don't store a default SessionBuilder when a CurrentTenantIdentifierResolver is provided return tenantIdResolver == null ? withOptions() : null; } - private SessionBuilderImpl buildTemporarySessionOpenOptions() { + private SessionBuilderImplementor buildTemporarySessionOpenOptions() { return withOptions() .autoClose( false ) .flushMode( FlushMode.MANUAL ) @@ -535,7 +523,7 @@ public SessionImplementor openSession() { } @Override - public SessionImpl openTemporarySession() { + public SessionImplementor openTemporarySession() { // The temporarySessionOpenOptions can't be used in some cases; // for example when using a TenantIdentifierResolver. return temporarySessionOpenOptions != null @@ -552,7 +540,7 @@ public Session getCurrentSession() { } @Override - public SessionBuilderImpl withOptions() { + public SessionBuilderImplementor withOptions() { return new SessionBuilderImpl( this ); } @@ -1104,459 +1092,13 @@ public static Interceptor configuredInterceptor(Interceptor interceptor, boolean return null; } - private Object resolveTenantIdentifier() { + public Object resolveTenantIdentifier() { final var resolver = getCurrentTenantIdentifierResolver(); return resolver != null ? resolver.resolveCurrentTenantIdentifier() : null; } - public static class SessionBuilderImpl implements SessionBuilderImplementor, SessionCreationOptions { - private static final Logger log = CoreLogging.logger( SessionBuilderImpl.class ); - - private final SessionFactoryImpl sessionFactory; - private Interceptor interceptor; - private StatementInspector statementInspector; - private Connection connection; - private PhysicalConnectionHandlingMode connectionHandlingMode; - private boolean autoJoinTransactions = true; - private FlushMode flushMode; - private boolean autoClose; - private boolean autoClear; - private Object tenantIdentifier; - private boolean readOnly; - private CacheMode cacheMode; - private boolean identifierRollback; - private TimeZone jdbcTimeZone; - private boolean explicitNoInterceptor; - private final int defaultBatchFetchSize; - private final boolean subselectFetchEnabled; - - // 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 - private final SessionOwnerBehavior sessionOwnerBehavior = SessionOwnerBehavior.LEGACY_NATIVE; - - public SessionBuilderImpl(SessionFactoryImpl sessionFactory) { - this.sessionFactory = sessionFactory; - - // set up default builder values... - final var options = sessionFactory.getSessionFactoryOptions(); - statementInspector = options.getStatementInspector(); - connectionHandlingMode = options.getPhysicalConnectionHandlingMode(); - autoClose = options.isAutoCloseSessionEnabled(); - defaultBatchFetchSize = options.getDefaultBatchFetchSize(); - subselectFetchEnabled = options.isSubselectFetchEnabled(); - identifierRollback = options.isIdentifierRollbackEnabled(); - cacheMode = options.getInitialSessionCacheMode(); - - tenantIdentifier = sessionFactory.resolveTenantIdentifier(); - jdbcTimeZone = options.getJdbcTimeZone(); - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // SessionCreationOptions - - @Override - public ExceptionMapper getExceptionMapper() { - return sessionOwnerBehavior == SessionOwnerBehavior.LEGACY_JPA - ? ExceptionMapperLegacyJpaImpl.INSTANCE - : null; - } - - @Override - public boolean shouldAutoJoinTransactions() { - return autoJoinTransactions; - } - - @Override - public FlushMode getInitialSessionFlushMode() { - return flushMode; - } - - @Override - public boolean isSubselectFetchEnabled() { - return subselectFetchEnabled; - } - - @Override - public int getDefaultBatchFetchSize() { - return defaultBatchFetchSize; - } - - @Override - public boolean shouldAutoClose() { - return autoClose; - } - - @Override - public boolean shouldAutoClear() { - return autoClear; - } - - @Override - public Connection getConnection() { - return connection; - } - - @Override - public Interceptor getInterceptor() { - return configuredInterceptor( interceptor, explicitNoInterceptor, sessionFactory.getSessionFactoryOptions() ); - } - - @Override - public StatementInspector getStatementInspector() { - return statementInspector; - } - - @Override - public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() { - return connectionHandlingMode; - } - - @Override - public String getTenantIdentifier() { - return tenantIdentifier != null - ? sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ) - : null; - } - - @Override - public Object getTenantIdentifierValue() { - return tenantIdentifier; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - @Override - public CacheMode getInitialCacheMode() { - return cacheMode; - } - - @Override - public boolean isIdentifierRollbackEnabled() { - return identifierRollback; - } - - @Override - public TimeZone getJdbcTimeZone() { - return jdbcTimeZone; - } - - @Override - public List getCustomSessionEventListener() { - return listeners; - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // SessionBuilder - - @Override - public SessionImpl openSession() { - log.tracef( "Opening Hibernate Session. tenant=%s", tenantIdentifier ); - return new SessionImpl( sessionFactory, this ); - } - - @Override - public SessionBuilderImpl interceptor(Interceptor interceptor) { - this.interceptor = interceptor; - this.explicitNoInterceptor = false; - return this; - } - - @Override - public SessionBuilderImpl noInterceptor() { - this.interceptor = EmptyInterceptor.INSTANCE; - this.explicitNoInterceptor = true; - return this; - } - - @Override @Deprecated - public SessionBuilderImpl statementInspector(StatementInspector statementInspector) { - this.statementInspector = statementInspector; - return this; - } - - @Override - public SessionBuilderImpl statementInspector(UnaryOperator operator) { - this.statementInspector = operator::apply; - return this; - } - - @Override - public SessionBuilderImpl connection(Connection connection) { - this.connection = connection; - return this; - } - - @Override @Deprecated - public SessionBuilderImpl connectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) { - this.connectionHandlingMode = connectionHandlingMode; - return this; - } - - @Override - public SessionBuilderImpl connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { - this.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret( acquisitionMode, releaseMode); - return this; - } - - @Override - public SessionBuilderImpl autoJoinTransactions(boolean autoJoinTransactions) { - this.autoJoinTransactions = autoJoinTransactions; - return this; - } - - @Override - public SessionBuilderImpl autoClose(boolean autoClose) { - this.autoClose = autoClose; - return this; - } - - @Override - public SessionBuilderImpl autoClear(boolean autoClear) { - this.autoClear = autoClear; - return this; - } - - @Override - public SessionBuilderImpl flushMode(FlushMode flushMode) { - this.flushMode = flushMode; - return this; - } - - @Override @Deprecated(forRemoval = true) - public SessionBuilderImpl tenantIdentifier(String tenantIdentifier) { - this.tenantIdentifier = tenantIdentifier; - return this; - } - - @Override - public SessionBuilderImpl tenantIdentifier(Object tenantIdentifier) { - this.tenantIdentifier = tenantIdentifier; - return this; - } - - @Override - public SessionBuilderImpl readOnly(boolean readOnly) { - this.readOnly = readOnly; - return this; - } - - @Override - public SessionBuilder initialCacheMode(CacheMode cacheMode) { - this.cacheMode = cacheMode; - return this; - } - - @Override - public SessionBuilderImpl identifierRollback(boolean identifierRollback) { - this.identifierRollback = identifierRollback; - return this; - } - - @Override - public SessionBuilderImpl eventListeners(SessionEventListener... listeners) { - if ( this.listeners == null ) { - final var baselineListeners = - sessionFactory.getSessionFactoryOptions().buildSessionEventListeners(); - this.listeners = new ArrayList<>( baselineListeners.length + listeners.length ); - addAll( this.listeners, baselineListeners ); - } - addAll( this.listeners, listeners ); - return this; - } - - @Override - public SessionBuilderImpl clearEventListeners() { - if ( listeners == null ) { - //Needs to initialize explicitly to an empty list as otherwise "null" implies the default listeners will be applied - listeners = new ArrayList<>( 3 ); - } - else { - listeners.clear(); - } - return this; - } - - @Override - public SessionBuilderImpl jdbcTimeZone(TimeZone timeZone) { - jdbcTimeZone = timeZone; - return this; - } - } - - public static class StatelessSessionBuilderImpl implements StatelessSessionBuilder, SessionCreationOptions { - private final SessionFactoryImpl sessionFactory; - private StatementInspector statementInspector; - private Connection connection; - private PhysicalConnectionHandlingMode connectionHandlingMode; - private Object tenantIdentifier; - private boolean readOnly; - private CacheMode cacheMode; - - public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) { - this.sessionFactory = sessionFactory; - final var options = sessionFactory.getSessionFactoryOptions(); - statementInspector = options.getStatementInspector(); - cacheMode = options.getInitialSessionCacheMode(); - tenantIdentifier = sessionFactory.resolveTenantIdentifier(); - connectionHandlingMode = options.getPhysicalConnectionHandlingMode(); - } - - @Override - public StatelessSession openStatelessSession() { - return new StatelessSessionImpl( sessionFactory, this ); - } - - @Override - public StatelessSessionBuilder connection(Connection connection) { - this.connection = connection; - return this; - } - - @Override - public StatelessSessionBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { - this.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret( acquisitionMode, releaseMode); - return this; - } - - @Override @Deprecated - public StatelessSessionBuilder tenantIdentifier(String tenantIdentifier) { - this.tenantIdentifier = tenantIdentifier; - return this; - } - - @Override - public StatelessSessionBuilder tenantIdentifier(Object tenantIdentifier) { - this.tenantIdentifier = tenantIdentifier; - return this; - } - - @Override - public StatelessSessionBuilder readOnly(boolean readOnly) { - this.readOnly = readOnly; - return this; - } - - @Override - public StatelessSessionBuilder initialCacheMode(CacheMode cacheMode) { - this.cacheMode = cacheMode; - return this; - } - - @Override @Deprecated - public StatelessSessionBuilder statementInspector(StatementInspector statementInspector) { - this.statementInspector = statementInspector; - return this; - } - - @Override - public StatelessSessionBuilder statementInspector(UnaryOperator operator) { - this.statementInspector = operator::apply; - return this; - } - - @Override - public boolean shouldAutoJoinTransactions() { - return true; - } - - @Override - public FlushMode getInitialSessionFlushMode() { - return FlushMode.ALWAYS; - } - - @Override - public boolean isSubselectFetchEnabled() { - return false; - } - - @Override - public int getDefaultBatchFetchSize() { - return -1; - } - - @Override - public boolean shouldAutoClose() { - return false; - } - - @Override - public boolean shouldAutoClear() { - return false; - } - - @Override - public Connection getConnection() { - return connection; - } - - @Override - public Interceptor getInterceptor() { - return configuredInterceptor( EmptyInterceptor.INSTANCE, false, - sessionFactory.getSessionFactoryOptions() ); - } - - @Override - public boolean isIdentifierRollbackEnabled() { - // identifier rollback not yet implemented for StatelessSessions - return false; - } - - @Override - public StatementInspector getStatementInspector() { - return statementInspector; - } - - @Override - public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() { - return connectionHandlingMode; - } - - @Override - public String getTenantIdentifier() { - return tenantIdentifier == null ? null - : sessionFactory.getTenantIdentifierJavaType().toString( tenantIdentifier ); - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - @Override - public CacheMode getInitialCacheMode() { - return cacheMode; - } - - @Override - public Object getTenantIdentifierValue() { - return tenantIdentifier; - } - - @Override - public TimeZone getJdbcTimeZone() { - return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone(); - } - - @Override - public List getCustomSessionEventListener() { - return null; - } - - @Override - public ExceptionMapper getExceptionMapper() { - return null; - } - } - @Override public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() { return getSessionFactoryOptions().getCustomEntityDirtinessStrategy(); 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 08a3233577e3..65604023df58 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -23,13 +23,14 @@ import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.Metamodel; import org.hibernate.*; -import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.creation.internal.SessionCreationOptions; +import org.hibernate.engine.creation.internal.SharedSessionBuilderImpl; +import org.hibernate.engine.creation.internal.SharedSessionCreationOptions; import org.hibernate.engine.internal.PersistenceContexts; -import org.hibernate.engine.jdbc.spi.JdbcCoordinator; +import org.hibernate.engine.internal.TransactionCompletionCallbacksImpl; import org.hibernate.engine.spi.ActionQueue; -import org.hibernate.engine.spi.ActionQueue.TransactionCompletionProcesses; import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityHolder; @@ -64,8 +65,6 @@ import org.hibernate.query.UnknownSqlResultSetMappingException; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.resource.jdbc.spi.JdbcSessionOwner; -import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; -import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.resource.transaction.spi.TransactionObserver; @@ -79,15 +78,12 @@ import java.io.ObjectOutputStream; import java.io.Serial; import java.io.Serializable; -import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TimeZone; -import java.util.function.UnaryOperator; import static java.lang.Boolean.parseBoolean; import static java.lang.Integer.parseInt; @@ -239,9 +235,9 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { private void setUpTransactionCompletionProcesses(SessionCreationOptions options) { if ( options instanceof SharedSessionCreationOptions sharedOptions && sharedOptions.isTransactionCoordinatorShared() ) { - final TransactionCompletionProcesses processes = sharedOptions.getTransactionCompletionProcesses(); - if ( processes != null ) { - actionQueue.setTransactionCompletionProcesses( processes, true ); + final TransactionCompletionCallbacksImpl callbacks = sharedOptions.getTransactionCompletionCallbacks(); + if ( callbacks != null ) { + actionQueue.setTransactionCompletionCallbacks( callbacks, true ); } } } @@ -1867,11 +1863,6 @@ public ActionQueue getActionQueue() { return actionQueue; } - @Override - public void registerProcess(AfterTransactionCompletionProcess process) { - getActionQueue().registerProcess( process ); - } - @Override public PersistenceContext getPersistenceContext() { checkOpenOrWaitingForAutoClose(); @@ -2033,240 +2024,6 @@ public void afterTransactionCompletion(boolean successful, boolean delayed) { super.afterTransactionCompletion( successful, delayed ); } - private static class SharedSessionBuilderImpl - extends SessionFactoryImpl.SessionBuilderImpl - implements SharedSessionBuilder, SharedSessionCreationOptions { - private final SessionImpl session; - private boolean shareTransactionContext; - private boolean tenantIdChanged; - private boolean readOnlyChanged; - - private SharedSessionBuilderImpl(SessionImpl session) { - super( (SessionFactoryImpl) session.getFactory() ); - this.session = session; - super.tenantIdentifier( session.getTenantIdentifierValue() ); - super.identifierRollback( session.isIdentifierRollbackEnabled() ); - } - - @Override - public SessionImpl openSession() { - if ( session.getSessionFactoryOptions().isMultiTenancyEnabled() ) { - if ( shareTransactionContext ) { - if ( tenantIdChanged ) { - throw new SessionException( - "Cannot redefine the tenant identifier on a child session if the connection is reused" ); - } - if ( readOnlyChanged ) { - throw new SessionException( - "Cannot redefine the read-only mode on a child session if the connection is reused" ); - } - } - } - return super.openSession(); - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // SharedSessionBuilder - - - @Override @Deprecated(forRemoval = true) - public SharedSessionBuilderImpl tenantIdentifier(String tenantIdentifier) { - super.tenantIdentifier( tenantIdentifier ); - tenantIdChanged = true; - return this; - } - - @Override - public SharedSessionBuilderImpl tenantIdentifier(Object tenantIdentifier) { - super.tenantIdentifier( tenantIdentifier ); - tenantIdChanged = true; - return this; - } - - @Override - public SharedSessionBuilderImpl readOnly(boolean readOnly) { - super.readOnly( readOnly ); - readOnlyChanged = true; - return this; - } - - @Override - public SharedSessionBuilderImpl initialCacheMode(CacheMode cacheMode) { - super.initialCacheMode( cacheMode ); - return this; - } - - @Override - public SharedSessionBuilderImpl interceptor() { - super.interceptor( session.getInterceptor() ); - return this; - } - - @Override - public SharedSessionBuilderImpl interceptor(Interceptor interceptor) { - super.interceptor( interceptor ); - return this; - } - - @Override - public SharedSessionBuilderImpl noInterceptor() { - super.noInterceptor(); - return this; - } - - @Override - public SharedSessionBuilderImpl connection() { - this.shareTransactionContext = true; - return this; - } - - @Override - public SharedSessionBuilderImpl connection(Connection connection) { - super.connection( connection ); - return this; - } - - private PhysicalConnectionHandlingMode getConnectionHandlingMode() { - return session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode(); - } - - @Override - @Deprecated(since = "6.0") - public SharedSessionBuilderImpl connectionReleaseMode() { - final PhysicalConnectionHandlingMode handlingMode = - PhysicalConnectionHandlingMode.interpret( ConnectionAcquisitionMode.AS_NEEDED, - getConnectionHandlingMode().getReleaseMode() ); - connectionHandlingMode( handlingMode ); - return this; - } - - @Override - public SharedSessionBuilderImpl connectionHandlingMode() { - connectionHandlingMode( getConnectionHandlingMode() ); - return this; - } - - @Override - public SharedSessionBuilderImpl autoJoinTransactions() { - super.autoJoinTransactions( session.shouldAutoJoinTransaction() ); - return this; - } - - @Override - public SharedSessionBuilderImpl autoJoinTransactions(boolean autoJoinTransactions) { - super.autoJoinTransactions( autoJoinTransactions ); - return this; - } - - @Override - public SharedSessionBuilderImpl autoClose(boolean autoClose) { - super.autoClose( autoClose ); - return this; - } - - @Override - public SharedSessionBuilderImpl flushMode() { - flushMode( session.getHibernateFlushMode() ); - return this; - } - - @Override - public SharedSessionBuilderImpl autoClose() { - autoClose( session.isAutoCloseSessionEnabled() ); - return this; - } - - @Override - public SharedSessionBuilderImpl identifierRollback(boolean identifierRollback) { - super.identifierRollback( identifierRollback ); - return this; - } - - @Override - public SharedSessionBuilderImpl jdbcTimeZone(TimeZone timeZone) { - super.jdbcTimeZone(timeZone); - return this; - } - - @Override - public SharedSessionBuilderImpl clearEventListeners() { - super.clearEventListeners(); - return this; - } - - @Override - public SharedSessionBuilderImpl flushMode(FlushMode flushMode) { - super.flushMode(flushMode); - return this; - } - - @Override - public SharedSessionBuilderImpl autoClear(boolean autoClear) { - super.autoClear(autoClear); - return this; - } - - @Override @Deprecated - public SharedSessionBuilderImpl statementInspector(StatementInspector statementInspector) { - super.statementInspector(statementInspector); - return this; - } - - @Override - public SharedSessionBuilderImpl statementInspector(UnaryOperator operator) { - super.statementInspector(operator); - return this; - } - - @Override @Deprecated - public SharedSessionBuilderImpl connectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) { - super.connectionHandlingMode(connectionHandlingMode); - return this; - } - - @Override @Deprecated - public SharedSessionBuilderImpl connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode) { - super.connectionHandling(acquisitionMode, releaseMode); - return this; - } - - @Override - public SharedSessionBuilderImpl eventListeners(SessionEventListener... listeners) { - super.eventListeners(listeners); - return this; - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // SharedSessionCreationOptions - - @Override - public boolean isTransactionCoordinatorShared() { - return shareTransactionContext; - } - - @Override - public TransactionCoordinator getTransactionCoordinator() { - return shareTransactionContext ? session.getTransactionCoordinator() : null; - } - - @Override - public JdbcCoordinator getJdbcCoordinator() { - return shareTransactionContext ? session.getJdbcCoordinator() : null; - } - - @Override - public Transaction getTransaction() { - return shareTransactionContext ? session.getCurrentTransaction() : null; - } - - @Override - public TransactionCompletionProcesses getTransactionCompletionProcesses() { - return shareTransactionContext - ? session.getActionQueue().getTransactionCompletionProcesses() - : null; - } - } - @Override protected void addSharedSessionTransactionObserver(TransactionCoordinator transactionCoordinator) { transactionObserver = new TransactionObserver() { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index bc0d513c842f..203783bf5356 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -4,31 +4,34 @@ */ package org.hibernate.internal; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; - +import jakarta.persistence.EntityGraph; import jakarta.persistence.PersistenceException; +import jakarta.transaction.SystemException; import org.hibernate.AssertionFailure; +import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; +import org.hibernate.Interceptor; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.SessionEventListener; import org.hibernate.SessionException; import org.hibernate.StatelessSession; import org.hibernate.TransientObjectException; import org.hibernate.UnresolvableObjectException; -import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; -import org.hibernate.cache.CacheException; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.creation.internal.SessionCreationOptions; +import org.hibernate.engine.internal.TransactionCompletionCallbacksImpl; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.StatelessSessionImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.event.monitor.spi.DiagnosticEvent; @@ -67,11 +70,16 @@ import org.hibernate.loader.internal.CacheLoadHelper; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; - -import jakarta.persistence.EntityGraph; -import jakarta.transaction.SystemException; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.stat.spi.StatisticsImplementor; +import java.sql.Connection; +import java.util.List; +import java.util.Set; +import java.util.TimeZone; +import java.util.function.BiConsumer; + import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.engine.internal.PersistenceContexts.createPersistenceContext; @@ -110,7 +118,7 @@ * @author Gavin King * @author Steve Ebersole */ -public class StatelessSessionImpl extends AbstractSharedSessionContract implements StatelessSession { +public class StatelessSessionImpl extends AbstractSharedSessionContract implements StatelessSessionImplementor { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( StatelessSessionImpl.class ); public static final MultiIdLoadOptions MULTI_ID_LOAD_OPTIONS = new MultiLoadOptions(); @@ -118,13 +126,33 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen private final LoadQueryInfluencers influencers; private final PersistenceContext temporaryPersistenceContext; private final boolean connectionProvided; - private final List afterCompletions = new ArrayList<>(); + private final TransactionCompletionCallbacksImpl transactionCompletionCallbacks; private final EventListenerGroups eventListenerGroups; public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super( factory, options ); connectionProvided = options.getConnection() != null; + transactionCompletionCallbacks = new TransactionCompletionCallbacksImpl( this ); + temporaryPersistenceContext = createPersistenceContext( this ); + influencers = new LoadQueryInfluencers( getFactory() ); + eventListenerGroups = factory.getEventListenerGroups(); + setUpMultitenancy( factory, influencers ); + // a nonzero batch size forces use of write-behind + // therefore ignore the value of hibernate.jdbc.batch_size + setJdbcBatchSize( 0 ); + } + + /** + * Form used when creating a "shared" stateless session. + */ + public StatelessSessionImpl(SessionFactoryImplementor factory, CommonSharedSessionCreationOptions options) { + super( + (SessionFactoryImpl) factory, + CommonSharedSessionCreationOptionsWrapper.wrapOptions( (SessionFactoryImpl) factory, options ) + ); + connectionProvided = false; + transactionCompletionCallbacks = new TransactionCompletionCallbacksImpl( this ); temporaryPersistenceContext = createPersistenceContext( this ); influencers = new LoadQueryInfluencers( getFactory() ); eventListenerGroups = factory.getEventListenerGroups(); @@ -551,12 +579,14 @@ protected Object idToUpsert(Object entity, EntityPersister persister) { // Hibernate Reactive may need to call this protected boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) { + getFactory().getEventEngine().getCallbackRegistry().preCreate( entity ); + if ( eventListenerGroups.eventListenerGroup_PRE_INSERT.isEmpty() ) { return false; } else { boolean veto = false; - final var event = new PreInsertEvent( entity, id, state, persister, null ); + final var event = new PreInsertEvent( entity, id, state, persister, this ); for ( var listener : eventListenerGroups.eventListenerGroup_PRE_INSERT.listeners() ) { veto |= listener.onPreInsert( event ); } @@ -566,12 +596,14 @@ protected boolean firePreInsert(Object entity, Object id, Object[] state, Entity // Hibernate Reactive may need to call this protected boolean firePreUpdate(Object entity, Object id, Object[] state, EntityPersister persister) { + getFactory().getEventEngine().getCallbackRegistry().preUpdate( entity ); + if ( eventListenerGroups.eventListenerGroup_PRE_UPDATE.isEmpty() ) { return false; } else { boolean veto = false; - final var event = new PreUpdateEvent( entity, id, state, null, persister, null ); + final var event = new PreUpdateEvent( entity, id, state, null, persister, this ); for ( var listener : eventListenerGroups.eventListenerGroup_PRE_UPDATE.listeners() ) { veto |= listener.onPreUpdate( event ); } @@ -586,7 +618,7 @@ protected boolean firePreUpsert(Object entity, Object id, Object[] state, Entity } else { boolean veto = false; - final var event = new PreUpsertEvent( entity, id, state, persister, null ); + final var event = new PreUpsertEvent( entity, id, state, persister, this ); for ( var listener : eventListenerGroups.eventListenerGroup_PRE_UPSERT.listeners() ) { veto |= listener.onPreUpsert( event ); } @@ -596,12 +628,14 @@ protected boolean firePreUpsert(Object entity, Object id, Object[] state, Entity // Hibernate Reactive may need to call this protected boolean firePreDelete(Object entity, Object id, EntityPersister persister) { + getFactory().getEventEngine().getCallbackRegistry().preRemove( entity ); + if ( eventListenerGroups.eventListenerGroup_PRE_DELETE.isEmpty() ) { return false; } else { boolean veto = false; - final var event = new PreDeleteEvent( entity, id, null, persister, null ); + final var event = new PreDeleteEvent( entity, id, null, persister, this ); for ( var listener : eventListenerGroups.eventListenerGroup_PRE_DELETE.listeners() ) { veto |= listener.onPreDelete( event ); } @@ -612,28 +646,28 @@ protected boolean firePreDelete(Object entity, Object id, EntityPersister persis // Hibernate Reactive may need to call this protected void firePostInsert(Object entity, Object id, Object[] state, EntityPersister persister) { eventListenerGroups.eventListenerGroup_POST_INSERT.fireLazyEventOnEachListener( - () -> new PostInsertEvent( entity, id, state, persister, null ), + () -> new PostInsertEvent( entity, id, state, persister, this ), PostInsertEventListener::onPostInsert ); } // Hibernate Reactive may need to call this protected void firePostUpdate(Object entity, Object id, Object[] state, EntityPersister persister) { eventListenerGroups.eventListenerGroup_POST_UPDATE.fireLazyEventOnEachListener( - () -> new PostUpdateEvent( entity, id, state, null, null, persister, null ), + () -> new PostUpdateEvent( entity, id, state, null, null, persister, this ), PostUpdateEventListener::onPostUpdate ); } // Hibernate Reactive may need to call this protected void firePostUpsert(Object entity, Object id, Object[] state, EntityPersister persister) { eventListenerGroups.eventListenerGroup_POST_UPSERT.fireLazyEventOnEachListener( - () -> new PostUpsertEvent( entity, id, state, null, persister, null ), + () -> new PostUpsertEvent( entity, id, state, null, persister, this ), PostUpsertEventListener::onPostUpsert ); } // Hibernate Reactive may need to call this protected void firePostDelete(Object entity, Object id, EntityPersister persister) { eventListenerGroups.eventListenerGroup_POST_DELETE.fireLazyEventOnEachListener( - () -> new PostDeleteEvent( entity, id, null, persister, null ), + () -> new PostDeleteEvent( entity, id, null, persister, this ), PostDeleteEventListener::onPostDelete ); } @@ -1301,40 +1335,30 @@ public void afterTransactionBegin() { @Override public void beforeTransactionCompletion() { + transactionCompletionCallbacks.beforeTransactionCompletion(); flushBeforeTransactionCompletion(); beforeTransactionCompletionEvents(); } @Override public void afterTransactionCompletion(boolean successful, boolean delayed) { - processAfterCompletions( successful ); + transactionCompletionCallbacks.afterTransactionCompletion( successful ); afterTransactionCompletionEvents( successful ); if ( shouldAutoClose() && !isClosed() ) { managedClose(); } } - private void processAfterCompletions(boolean successful) { - for ( AfterTransactionCompletionProcess completion: afterCompletions ) { - try { - completion.doAfterTransactionCompletion( successful, this ); - } - catch (CacheException ce) { - LOG.unableToReleaseCacheLock( ce ); - // continue loop - } - catch (Exception e) { - throw new HibernateException( "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e ); - } - } - afterCompletions.clear(); - } - @Override public boolean isTransactionInProgress() { return connectionProvided || super.isTransactionInProgress(); } + @Override + public TransactionCompletionCallbacks getTransactionCompletionCallbacks() { + return transactionCompletionCallbacks; + } + @Override public void flushBeforeTransactionCompletion() { final boolean flush; @@ -1369,7 +1393,9 @@ protected Object lockCacheItem(Object id, Object previousVersion, EntityPersiste getTenantIdentifier() ); final SoftLock lock = cache.lockItem( this, ck, previousVersion ); - afterCompletions.add( (success, session) -> cache.unlockItem( session, ck, lock ) ); + transactionCompletionCallbacks.registerCallback( (success, session) -> { + cache.unlockItem( session, ck, lock ); + } ); return ck; } else { @@ -1393,7 +1419,9 @@ protected Object lockCacheItem(Object key, CollectionPersister persister) { getTenantIdentifier() ); final SoftLock lock = cache.lockItem( this, ck, null ); - afterCompletions.add( (success, session) -> cache.unlockItem( this, ck, lock ) ); + transactionCompletionCallbacks.registerCallback( (success, session) -> { + cache.unlockItem( this, ck, lock ); + } ); return ck; } else { @@ -1407,11 +1435,6 @@ protected void removeCacheItem(Object ck, CollectionPersister persister) { } } - @Override - public void registerProcess(AfterTransactionCompletionProcess process) { - afterCompletions.add( process ); - } - @Override public void lock(String entityName, Object child, LockOptions lockOptions) { final var persister = getEntityPersister( entityName, child ); @@ -1486,4 +1509,110 @@ public Integer getBatchSize() { return null; } } + + /** + * Wraps a CommonSharedSessionCreationOptions as a SessionCreationOptions to pass + * to AbstractSharedSessionContract during construction. + * + * @param factory The SessionFactory + * @param options The CommonSharedSessionCreationOptions being wrapped. + */ + private record CommonSharedSessionCreationOptionsWrapper( + SessionFactoryImplementor factory, + CommonSharedSessionCreationOptions options) + implements SessionCreationOptions { + + private static SessionCreationOptions wrapOptions(SessionFactoryImpl factory, CommonSharedSessionCreationOptions options) { + return new CommonSharedSessionCreationOptionsWrapper( factory, options ); + } + + @Override + public Interceptor getInterceptor() { + return options.getInterceptor(); + } + + @Override + public StatementInspector getStatementInspector() { + return options.getStatementInspector(); + } + + @Override + public Object getTenantIdentifierValue() { + return options.getTenantIdentifierValue(); + } + + @Override + public boolean isReadOnly() { + return options.isReadOnly(); + } + + @Override + public CacheMode getInitialCacheMode() { + return options.getInitialCacheMode(); + } + + @Override + public boolean shouldAutoJoinTransactions() { + return true; + } + + @Override + public FlushMode getInitialSessionFlushMode() { + return FlushMode.ALWAYS; + } + + @Override + public boolean isSubselectFetchEnabled() { + return false; + } + + @Override + public int getDefaultBatchFetchSize() { + return -1; + } + + @Override + public boolean shouldAutoClose() { + return false; + } + + @Override + public boolean shouldAutoClear() { + return false; + } + + @Override + public Connection getConnection() { + return null; + } + + @Override + public boolean isIdentifierRollbackEnabled() { + // identifier rollback not yet implemented for StatelessSessions + return false; + } + + @Override + public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() { + return factory.getSessionFactoryOptions().getPhysicalConnectionHandlingMode(); + } + + @Override + public String getTenantIdentifier() { + final Object tenantIdentifier = getTenantIdentifierValue(); + return tenantIdentifier == null + ? null + : factory.getTenantIdentifierJavaType().toString( tenantIdentifier ); + } + + @Override + public TimeZone getJdbcTimeZone() { + return factory.getSessionFactoryOptions().getJdbcTimeZone(); + } + + @Override + public List getCustomSessionEventListener() { + return null; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/StatementInspector.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/StatementInspector.java index 5e5a6c303861..d833e00d176a 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/StatementInspector.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/StatementInspector.java @@ -4,6 +4,7 @@ */ package org.hibernate.resource.jdbc.spi; + import java.io.Serializable; /** @@ -30,6 +31,8 @@ * @author Steve Ebersole */ public interface StatementInspector extends Serializable { + StatementInspector NONE = sql -> sql; + /** * Inspect the given SQL command, possibly returning a different * SQL command to be used instead. A {@code null} return value is diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomAfterCompletionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomAfterCompletionTest.java index a84ef9158785..2fd6cb703dd0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomAfterCompletionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomAfterCompletionTest.java @@ -30,7 +30,7 @@ public class CustomAfterCompletionTest extends BaseCoreFunctionalTestCase { public void success() { inSession( session -> { AtomicBoolean called = new AtomicBoolean( false ); - session.getActionQueue().registerProcess( new AfterTransactionCompletionProcess() { + session.getActionQueue().registerCallback( new AfterTransactionCompletionProcess() { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { called.set( true ); @@ -56,7 +56,7 @@ public void doAfterTransactionCompletion(boolean success, SharedSessionContractI public void failure() { try { inSession( session -> { - session.getActionQueue().registerProcess( new AfterTransactionCompletionProcess() { + session.getActionQueue().registerCallback( new AfterTransactionCompletionProcess() { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { throw new RuntimeException( "My exception" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomBeforeCompletionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomBeforeCompletionTest.java index cd1e2187dc13..4aa869f83806 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomBeforeCompletionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/actionqueue/CustomBeforeCompletionTest.java @@ -11,8 +11,8 @@ import org.hibernate.HibernateException; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Assert; @@ -30,7 +30,7 @@ public class CustomBeforeCompletionTest extends BaseCoreFunctionalTestCase { public void success() { inSession( session -> { AtomicBoolean called = new AtomicBoolean( false ); - session.getActionQueue().registerProcess( s -> called.set( true ) ); + session.getActionQueue().registerCallback( s -> called.set( true ) ); Assert.assertFalse( called.get() ); inTransaction( session, theSession -> { theSession.persist( new SimpleEntity( "jack" ) ); @@ -51,9 +51,9 @@ public void success() { public void failure() { try { inSession( session -> { - session.getActionQueue().registerProcess( new BeforeTransactionCompletionProcess() { + session.getActionQueue().registerCallback( new BeforeTransactionCompletionProcess() { @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { throw new RuntimeException( "My exception" ); } } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/delegation/TestDelegatingSessionBuilderImplementor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/delegation/TestDelegatingSessionBuilderImplementor.java index d29e51fc6ab5..9a0473b91236 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/delegation/TestDelegatingSessionBuilderImplementor.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/delegation/TestDelegatingSessionBuilderImplementor.java @@ -5,7 +5,7 @@ package org.hibernate.orm.test.delegation; import org.hibernate.engine.spi.AbstractDelegatingSessionBuilderImplementor; -import org.hibernate.engine.spi.SessionBuilderImplementor; +import org.hibernate.engine.creation.spi.SessionBuilderImplementor; /** * If this class does not compile anymore due to unimplemented methods, you should probably add the corresponding diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessCallbacksTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessCallbacksTest.java index c49feda578dc..2cb02b653181 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessCallbacksTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessCallbacksTest.java @@ -18,7 +18,6 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @SessionFactory @@ -29,17 +28,13 @@ public class StatelessCallbacksTest { WithCallbacks instance = new WithCallbacks(); instance.name = "gavin"; s.insert(instance); - // because the semantics of @PrePersist and @PreRemove - // are inappropriate for a StatelessSession, the @Pre - // don't get called. However, the @Post events do make - // sense, since they correspond to database operations - assertFalse(instance.prePersist); + assertTrue(instance.prePersist); assertTrue(instance.postPersist); s.update(instance); - assertFalse(instance.preUpdate); + assertTrue(instance.preUpdate); assertTrue(instance.postUpdate); s.delete(instance); - assertFalse(instance.preRemove); + assertTrue(instance.preRemove); assertTrue(instance.postRemove); }); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/events/StatelessSessionCallbacksTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/events/StatelessSessionCallbacksTests.java new file mode 100644 index 000000000000..cd3a6f416af2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/events/StatelessSessionCallbacksTests.java @@ -0,0 +1,169 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.stateless.events; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Table; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@DomainModel(annotatedClasses = StatelessSessionCallbacksTests.Person.class) +@SessionFactory +public class StatelessSessionCallbacksTests { + + @Test + void simpleSession(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + final Person john = new Person( 1, "John" ); + session.persist( john ); + } ); + + checkCallbackCounts( 1, 0, 0 ); + + factoryScope.inTransaction( (session) -> { + final Person john = session.find( Person.class, 1 ); + john.name = "Jonathan"; + } ); + + checkCallbackCounts( 1, 1, 0 ); + + factoryScope.inTransaction( (session) -> { + final Person john = session.find( Person.class, 1 ); + session.remove( john ); + } ); + + checkCallbackCounts( 1, 1, 1 ); + } + + @Test + void simpleStatelessSession(SessionFactoryScope factoryScope) { + factoryScope.inStatelessTransaction( (session) -> { + final Person john = new Person( 1, "John" ); + session.insert( john ); + } ); + + checkCallbackCounts( 1, 0, 0 ); + + factoryScope.inStatelessTransaction( (session) -> { + final Person john = session.get( Person.class, 1 ); + john.name = "Jonathan"; + session.update( john ); + } ); + + checkCallbackCounts( 1, 1, 0 ); + + factoryScope.inStatelessTransaction( (session) -> { + final Person john = session.get( Person.class, 1 ); + session.delete( john ); + } ); + + checkCallbackCounts( 1, 1, 1 ); + } + + private void checkCallbackCounts(int insert, int update, int delete) { + assertThat( Person.beforeInsertCalls ).isEqualTo( insert ); + assertThat( Person.afterInsertCalls ).isEqualTo( insert ); + + assertThat( Person.beforeUpdateCalls ).isEqualTo( update ); + assertThat( Person.afterUpdateCalls ).isEqualTo( update ); + + assertThat( Person.beforeDeleteCalls ).isEqualTo( delete ); + assertThat( Person.afterDeleteCalls ).isEqualTo( delete ); + + } + + @BeforeEach + void setUp() { + Person.resetCallbackState(); + } + + @AfterEach + void dropTestData(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + @Entity(name="Person") + @Table(name="persons") + public static class Person { + public static int beforeInsertCalls; + public static int afterInsertCalls; + + public static int beforeUpdateCalls; + public static int afterUpdateCalls; + + public static int beforeDeleteCalls; + public static int afterDeleteCalls; + + @Id + private Integer id; + private String name; + + public Person() { + } + + public Person(Integer id, String name) { + this.id = id; + this.name = name; + } + + @PrePersist + public void beforeInsert() { + beforeInsertCalls++; + } + + @PostPersist + public void afterInsert() { + afterInsertCalls++; + } + + @PreUpdate + public void beforeUpdate() { + beforeUpdateCalls++; + } + + @PostUpdate + public void afterUpdate() { + afterUpdateCalls++; + } + + @PreRemove + public void beforeDelete() { + beforeDeleteCalls++; + } + + @PostRemove + public void afterDelete() { + afterDeleteCalls++; + } + + public static void resetCallbackState() { + beforeInsertCalls = 0; + afterInsertCalls = 0; + + beforeUpdateCalls = 0; + afterUpdateCalls = 0; + + beforeDeleteCalls = 0; + afterDeleteCalls = 0; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java new file mode 100644 index 000000000000..5e7cc35b8939 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.stateless.shared; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.Interceptor; +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.hibernate.engine.spi.StatelessSessionImplementor; +import org.hibernate.orm.test.interceptor.StatefulInterceptor; +import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@DomainModel(annotatedClasses = SimpleSharedStatelessSessionBuildingTests.Something.class) +@SessionFactory(useCollectingStatementInspector = true, interceptorClass = StatefulInterceptor.class) +public class SimpleSharedStatelessSessionBuildingTests { + + @Test + void testStatementInspector(SessionFactoryScope factoryScope) { + final SQLStatementInspector sqlCollector = factoryScope.getCollectingStatementInspector(); + + // apply a special StatementInspector to the base and check the behavior of the various options + final StatementInspectorImpl appliedToBase = new StatementInspectorImpl( "Applied to base" ); + + try (Session base = factoryScope.getSessionFactory() + .withOptions() + .statementInspector( appliedToBase ) + .openSession()) { + + // baseline: + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base. + statelessWithOptions() + .open()) { + assertThat( nested.getJdbcSessionContext().getStatementInspector() ).isSameAs( sqlCollector ); + } + + // 1. noStatementInspector + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base + .statelessWithOptions() + .noStatementInspector() + .open()) { + assertThat( nested.getJdbcSessionContext().getStatementInspector() ).isNotSameAs( sqlCollector ); + assertThat( nested.getJdbcSessionContext().getStatementInspector() ).isNotSameAs( appliedToBase ); + } + + // 2. statementInspector() + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base + .statelessWithOptions() + .statementInspector() + .open()) { + assertThat( nested.getJdbcSessionContext().getStatementInspector() ).isSameAs( appliedToBase ); + } + } + } + + @Test + void testInterceptor(SessionFactoryScope factoryScope) { + final Interceptor sfInterceptor = factoryScope.getSessionFactory().getSessionFactoryOptions().getInterceptor(); + + // apply a special StatementInspector to the base and check the behavior of the various options + final InterceptorImpl appliedToBase = new InterceptorImpl( "Applied to base" ); + + try (Session base = factoryScope.getSessionFactory() + .withOptions() + .interceptor( appliedToBase ) + .openSession()) { + + // baseline - should use the Interceptor from SF + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base. + statelessWithOptions() + .open()) { + assertThat( nested.getInterceptor() ).isSameAs( sfInterceptor ); + } + + // 1. noInterceptor() - should use no (Empty)Interceptor + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base + .statelessWithOptions() + .noInterceptor() + .open()) { + assertThat( nested.getInterceptor() ).isNotSameAs( sfInterceptor ); + assertThat( nested.getInterceptor() ).isNotSameAs( appliedToBase ); + } + + // 2. interceptor() - should share the interceptor from the base session + try (StatelessSessionImplementor nested = (StatelessSessionImplementor) base + .statelessWithOptions() + .interceptor() + .open()) { + assertThat( nested.getInterceptor() ).isSameAs( appliedToBase ); + } + } + } + + @Test + void testUsage(SessionFactoryScope factoryScope) { + final SQLStatementInspector sqlCollector = factoryScope.getCollectingStatementInspector(); + + // try from Session + sqlCollector.clear(); + factoryScope.inTransaction( (statefulSession) -> { + try (StatelessSession statelessSession = statefulSession + .statelessWithOptions() + .connection() + .open()) { + statelessSession.insert( new Something( 1, "first" ) ); + } + } ); + + // try from StatelessSession + sqlCollector.clear(); + factoryScope.inStatelessTransaction( (statelessSession) -> { + try (StatelessSession ss2 = statelessSession + .statelessWithOptions() + .connection() + .open()) { + ss2.insert( new Something( 2, "first" ) ); + } + } ); + + assertThat( sqlCollector.getSqlQueries() ).hasSize( 1 ); + } + + @AfterEach + void dropTestData(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + @Entity(name="Something") + @Table(name="somethings") + public static class Something { + @Id + private Integer id; + private String name; + + public Something() { + } + + public Something(Integer id, String name) { + this.id = id; + this.name = name; + } + } + + public record StatementInspectorImpl(String name) implements StatementInspector { + @Override + public String inspect(String sql) { + return sql; + } + } + + public record InterceptorImpl(String name) implements Interceptor { + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tm/JtaAfterCompletionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tm/JtaAfterCompletionTest.java index b89dabe50869..3a748e4999c7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tm/JtaAfterCompletionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tm/JtaAfterCompletionTest.java @@ -77,8 +77,8 @@ public void testAfterCompletionCallbackExecutedAfterTransactionTimeout() throws // The after tracks whether it is invoked since this test is to guarantee it is called final SessionImplementor sessionImplementor = (SessionImplementor) session; final ActionQueue actionQueue = sessionImplementor.getActionQueue(); - actionQueue.registerProcess( new AfterCallbackCompletionHandler() ); - actionQueue.registerProcess( new BeforeCallbackCompletionHandler() ); + actionQueue.registerCallback( new AfterCallbackCompletionHandler() ); + actionQueue.registerCallback( new BeforeCallbackCompletionHandler() ); TestingJtaPlatformImpl.transactionManager().commit(); } @@ -104,7 +104,7 @@ public void testAfterCompletionCallbackExecutedAfterTransactionTimeout() throws public static class BeforeCallbackCompletionHandler implements BeforeTransactionCompletionProcess { @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { try { // Wait for the transaction to be rolled back by the Reaper thread. final Transaction transaction = TestingJtaPlatformImpl.transactionManager().getTransaction(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java index 9ad4bc106cad..3da2a85fff57 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java @@ -41,6 +41,7 @@ import org.hibernate.envers.internal.revisioninfo.RevisionInfoNumberReader; import org.hibernate.envers.internal.revisioninfo.RevisionInfoQueryCreator; import org.hibernate.envers.internal.revisioninfo.RevisionTimestampValueResolver; +import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.models.spi.ClassDetails; @@ -72,6 +73,7 @@ public class RevisionInfoConfiguration { private final RevisionInfoQueryCreator revisionInfoQueryCreator; private final ModifiedEntityNamesReader modifiedEntityNamesReader; private final String revisionInfoEntityName; + private final String revisionInfoQueryName; private final PropertyData revisionInfoTimestampData; private final String revisionInfoTimestampTypeName; private final String revisionPropType; @@ -89,6 +91,7 @@ public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector // initialize attributes from resolver this.revisionInfoClass = resolver.revisionInfoClass; this.revisionInfoEntityName = resolver.revisionInfoEntityName; + this.revisionInfoQueryName = resolver.revisionInfoQueryName; this.revisionPropType = resolver.revisionPropType; this.revisionPropSqlType = resolver.revisionPropSqlType; this.revisionInfoTimestampData = resolver.revisionInfoTimestampData; @@ -106,7 +109,7 @@ public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector ); revisionInfoQueryCreator = new RevisionInfoQueryCreator( - resolver.revisionInfoEntityName, + resolver.revisionInfoQueryName, resolver.revisionInfoIdData.getName(), resolver.timestampValueResolver ); @@ -123,10 +126,20 @@ public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector } } + /** + * The full entity name for the revision entity. + */ public String getRevisionInfoEntityName() { return revisionInfoEntityName; } + /** + * The query name for the revision entity. + */ + public String getRevisionInfoQueryName() { + return revisionInfoQueryName; + } + public String getRevisionInfoPropertyType() { return revisionPropType; } @@ -269,6 +282,7 @@ private class RevisionEntityResolver { private boolean revisionTimestampFound; private boolean modifiedEntityNamesFound; private String revisionInfoEntityName; + private String revisionInfoQueryName; private Class revisionInfoClass; private Class revisionListenerClass; private RevisionInfoGenerator revisionInfoGenerator; @@ -335,6 +349,7 @@ private void locateRevisionEntityMapping() { } revisionInfoEntityName = persistentClass.getEntityName(); + revisionInfoQueryName = determineQueryName( persistentClass ); revisionInfoClass = persistentClass.getMappedClass(); revisionListenerClass = getRevisionListenerClass( revisionEntity.value() ); @@ -387,8 +402,8 @@ private void locateRevisionEntityMapping() { : SequenceIdRevisionEntity.class; } - // Use the simple name of default revision entities as entity name - revisionInfoEntityName = revisionInfoClass.getSimpleName(); + revisionInfoQueryName = revisionInfoClass.getSimpleName(); + revisionInfoEntityName = revisionInfoClass.getName(); timestampValueResolver = createRevisionTimestampResolver( revisionInfoClass, @@ -421,6 +436,13 @@ private void locateRevisionEntityMapping() { } } + private String determineQueryName(PersistentClass persistentClass) { + if ( StringHelper.isNotEmpty( persistentClass.getJpaEntityName() ) ) { + return persistentClass.getJpaEntityName(); + } + return persistentClass.getEntityName(); + } + private boolean useEntityTrackingRevisionEntity(Class clazz) { return configuration.isTrackEntitiesChanged() || ( configuration.isNativeIdEnabled() && DefaultTrackingModifiedEntitiesRevisionEntity.class.isAssignableFrom( clazz ) ) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/event/spi/BaseEnversEventListener.java b/hibernate-envers/src/main/java/org/hibernate/envers/event/spi/BaseEnversEventListener.java index 3b014881a028..20111b395576 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/event/spi/BaseEnversEventListener.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/event/spi/BaseEnversEventListener.java @@ -6,7 +6,7 @@ import java.util.Set; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.RelationDescription; @@ -45,7 +45,7 @@ protected final void generateBidirectionalCollectionChangeWorkUnits( String entityName, Object[] newState, Object[] oldState, - SessionImplementor session) { + SharedSessionContractImplementor session) { // Checking if this is enabled in configuration ... if ( !enversService.getConfig().isGenerateRevisionsForCollections() ) { return; @@ -84,8 +84,11 @@ protected final void generateBidirectionalCollectionChangeWorkUnits( } private void addCollectionChangeWorkUnit( - AuditProcess auditProcess, SessionImplementor session, - String fromEntityName, RelationDescription relDesc, Object value) { + AuditProcess auditProcess, + SharedSessionContractImplementor session, + String fromEntityName, + RelationDescription relDesc, + Object value) { // relDesc.getToEntityName() doesn't always return the entity name of the value - in case // of subclasses, this will be root class, no the actual class. So it can't be used here. String toEntityName; @@ -128,9 +131,9 @@ private void addCollectionChangeWorkUnit( ); } - protected void checkIfTransactionInProgress(SessionImplementor session) { + protected void checkIfTransactionInProgress(SharedSessionContractImplementor session) { if ( !session.isTransactionInProgress() ) { - // Historical data would not be flushed to audit tables if outside of active transaction + // Historical data would not be flushed to audit tables if we are outside an active transaction // (AuditProcess#doBeforeTransactionCompletion(SessionImplementor) not executed). throw new AuditException( "Unable to create revision because of non-active transaction" ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ComponentPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ComponentPropertyMapper.java index a2e9cf09f820..05a641bc834c 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ComponentPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ComponentPropertyMapper.java @@ -4,13 +4,8 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.PropertyData; @@ -20,6 +15,11 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.Setter; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Michal Skowronek (mskowr at o2 dot pl) @@ -69,7 +69,7 @@ public void addComposite(PropertyData propertyData, PropertyMapper propertyMappe @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -78,7 +78,7 @@ public boolean mapToMapFromEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -251,9 +251,11 @@ private boolean isAllPropertiesNull(Map data) { @Override public List mapCollectionChanges( - SessionImplementor session, String referencingPropertyName, + SharedSessionContractImplementor session, + String referencingPropertyName, PersistentCollection newColl, - Serializable oldColl, Object id) { + Serializable oldColl, + Object id) { return delegate.mapCollectionChanges( session, referencingPropertyName, newColl, oldColl, id ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ExtendedPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ExtendedPropertyMapper.java index ccd932008488..066fe0dbff4d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ExtendedPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/ExtendedPropertyMapper.java @@ -6,14 +6,14 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; /** * @author Adam Warski (adam at warski dot org) */ public interface ExtendedPropertyMapper extends PropertyMapper, CompositeMapperBuilder { boolean map( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, String[] propertyNames, Object[] newState, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiDynamicComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiDynamicComponentMapper.java index fdd08ed30160..ea359a257bdd 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiDynamicComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiDynamicComponentMapper.java @@ -4,13 +4,13 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.reader.AuditReaderImplementor; +import java.util.Map; + /** * Multi mapper for dynamic components (it knows that component is a map, not a class) * @@ -43,7 +43,7 @@ public void add(PropertyData propertyData) { @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -69,7 +69,7 @@ private Object getValue(Object newObj, PropertyData propertyData) { @Override public boolean map( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, String[] propertyNames, Object[] newState, @@ -92,7 +92,7 @@ public boolean map( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiPropertyMapper.java index 2764175f7d01..f4a4b5000360 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/MultiPropertyMapper.java @@ -4,12 +4,8 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.reader.AuditReaderImplementor; @@ -20,6 +16,10 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.Getter; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Michal Skowronek (mskowr at o2 dot pl) @@ -74,7 +74,7 @@ protected Object getAtIndexOrNull(Object[] array, int index) { @Override public boolean map( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, String[] propertyNames, Object[] newState, @@ -97,7 +97,7 @@ public boolean map( @Override public boolean mapToMapFromEntity( - final SessionImplementor session, + final SharedSessionContractImplementor session, final Map data, final Object newObj, final Object oldObj) { @@ -141,7 +141,7 @@ else if ( oldObj != null ) { @Override public void mapModifiedFlagsToMapFromEntity( - final SessionImplementor session, + final SharedSessionContractImplementor session, final Map data, final Object newObj, final Object oldObj) { @@ -240,7 +240,7 @@ public void mapModifiedFlagsToMapForCollectionChange(String collectionPropertyNa @Override public List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, Object id) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/PropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/PropertyMapper.java index 75d52b05016f..348a4be2bc8d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/PropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/PropertyMapper.java @@ -4,15 +4,15 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.reader.AuditReaderImplementor; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Michal Skowronek (mskowr at o2 dot pl) @@ -29,7 +29,7 @@ public interface PropertyMapper extends ModifiedFlagMapperSupport, DynamicCompon * * @return True if there are any differences between the states represented by newObj and oldObj. */ - boolean mapToMapFromEntity(SessionImplementor session, Map data, Object newObj, Object oldObj); + boolean mapToMapFromEntity(SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj); /** * Maps properties from the given map to the given object. @@ -68,12 +68,13 @@ Object mapToEntityFromMap( * @return List of changes that need to be performed on the persistent store. */ List mapCollectionChanges( - SessionImplementor session, String referencingPropertyName, + SharedSessionContractImplementor session, + String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, Object id); void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java index 33c33804edea..14c7c51d50a6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SinglePropertyMapper.java @@ -4,16 +4,11 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.Objects; - import org.hibernate.HibernateException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.OracleDialect; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.PropertyData; @@ -23,6 +18,11 @@ import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.SetterFieldImpl; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Objects; + /** * TODO: diff * @@ -51,7 +51,7 @@ public void add(PropertyData propertyData) { @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -67,7 +67,7 @@ public boolean mapToMapFromEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -152,7 +152,7 @@ private boolean isPrimitive(Setter setter, PropertyData propertyData, Class c @Override public List mapCollectionChanges( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SubclassPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SubclassPropertyMapper.java index cd3da5238504..f7a1302d4e5f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SubclassPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/SubclassPropertyMapper.java @@ -4,18 +4,18 @@ */ package org.hibernate.envers.internal.entities.mapper; -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.metamodel.spi.EmbeddableInstantiator; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * A mapper which maps from a parent mapper and a "main" one, but adds only to the "main". The "main" mapper * should be the mapper of the subclass. @@ -35,7 +35,7 @@ public SubclassPropertyMapper(ExtendedPropertyMapper main, ExtendedPropertyMappe @Override public boolean map( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, String[] propertyNames, Object[] newState, @@ -48,7 +48,7 @@ public boolean map( @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -60,7 +60,7 @@ public boolean mapToMapFromEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -98,9 +98,11 @@ public Object mapToEntityFromMap( @Override public List mapCollectionChanges( - SessionImplementor session, String referencingPropertyName, + SharedSessionContractImplementor session, + String referencingPropertyName, PersistentCollection newColl, - Serializable oldColl, Object id) { + Serializable oldColl, + Object id) { final List parentCollectionChanges = parentMapper.mapCollectionChanges( session, referencingPropertyName, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java index 28a14ce3ba70..6e5ce9be44cf 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java @@ -4,13 +4,13 @@ */ package org.hibernate.envers.internal.entities.mapper.id; -import java.util.List; -import java.util.Map; - -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.service.ServiceRegistry; +import java.util.List; +import java.util.Map; + /** * Base contract for all identifier mappers. * @@ -22,7 +22,7 @@ public interface IdMapper { void mapToMapFromId(Map data, Object obj); - default void mapToMapFromId(Session session, Map data, Object obj) { + default void mapToMapFromId(SharedSessionContractImplementor session, Map data, Object obj) { // Delegate to the old behavior, allowing implementations to override. mapToMapFromId( data, obj ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java index 5cdc253414a4..93174d888753 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java @@ -4,18 +4,18 @@ */ package org.hibernate.envers.internal.entities.mapper.id; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.mapping.Component; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.service.ServiceRegistry; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + /** * An implementation of an identifier mapper for {@link jakarta.persistence.IdClass} or multiple * {@link jakarta.persistence.Id} identifier mappings. @@ -43,7 +43,7 @@ public void add(PropertyData propertyData) { } @Override - public void mapToMapFromId(Session session, Map data, Object obj) { + public void mapToMapFromId(SharedSessionContractImplementor session, Map data, Object obj) { if ( compositeIdClass.isInstance( obj ) ) { if ( embedded ) { final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( obj ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/VirtualEntitySingleIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/VirtualEntitySingleIdMapper.java index ce25129b5b3b..74254d92f5d2 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/VirtualEntitySingleIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/VirtualEntitySingleIdMapper.java @@ -6,11 +6,12 @@ import java.util.Map; -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.EntitiesConfigurations; import org.hibernate.envers.internal.entities.EntityConfiguration; import org.hibernate.envers.internal.entities.PropertyData; +import org.hibernate.envers.internal.tools.OrmTools; import org.hibernate.envers.internal.tools.ReflectionTools; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Setter; @@ -44,12 +45,12 @@ public VirtualEntitySingleIdMapper(ServiceRegistry serviceRegistry, PropertyData } @Override - public void mapToMapFromId(Session session, Map data, Object obj) { + public void mapToMapFromId(SharedSessionContractImplementor session, Map data, Object obj) { final Object value = getValueFromObject( propertyData, obj ); // Either loads the entity from the session's 1LC if it already exists or potentially creates a // proxy object to represent the entity by identifier so that we can reference it in the map. - final Object entity = session.getReference( this.entityName, value ); + final Object entity = OrmTools.loadAuditEntity( this.entityName, value, session ); data.put( propertyData.getName(), entity ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractCollectionMapper.java index 82da4496cf45..5511647ac50b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractCollectionMapper.java @@ -16,7 +16,7 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionEntry; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; @@ -77,7 +77,7 @@ protected AbstractCollectionMapper( * @param changed The changed collection element to map. */ protected abstract void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object changed); @@ -99,7 +99,7 @@ protected Map createIdMap(int ordinal) { } protected void addCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, List collectionChanges, Set changed, RevisionType revisionType, @@ -130,7 +130,7 @@ protected void addCollectionChanges( @Override @SuppressWarnings("unchecked") public List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, @@ -145,7 +145,7 @@ public List mapCollectionChanges( @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -155,7 +155,7 @@ public boolean mapToMapFromEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -221,7 +221,7 @@ protected abstract Initializor getInitializor( boolean removed); protected CollectionPersister resolveCollectionPersister( - SessionImplementor session, + SharedSessionContractImplementor session, PersistentCollection collection) { // First attempt to resolve the persister from the collection entry if ( collection != null ) { @@ -324,7 +324,7 @@ public Object mapToEntityFromMap( * @return the persistent collection changes. */ protected abstract List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, PersistentCollection newColl, Serializable oldColl, Object id); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractOneToOneMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractOneToOneMapper.java index eb7ae02db265..fd58143b1a76 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractOneToOneMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractOneToOneMapper.java @@ -4,18 +4,18 @@ */ package org.hibernate.envers.internal.entities.mapper.relation; -import java.io.Serializable; -import java.util.Map; import jakarta.persistence.NoResultException; - import org.hibernate.NonUniqueResultException; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.reader.AuditReaderImplementor; import org.hibernate.service.ServiceRegistry; +import java.io.Serializable; +import java.util.Map; + /** * Template class for property mappers that manage one-to-one relation. * @@ -86,7 +86,7 @@ protected abstract Object queryForReferencedEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractToOneMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractToOneMapper.java index d09da38d053d..ea8c7b81645e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractToOneMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/AbstractToOneMapper.java @@ -4,12 +4,8 @@ */ package org.hibernate.envers.internal.entities.mapper.relation; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.EntityConfiguration; import org.hibernate.envers.internal.entities.PropertyData; @@ -19,6 +15,10 @@ import org.hibernate.envers.internal.tools.ReflectionTools; import org.hibernate.service.ServiceRegistry; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + /** * Base class for property mappers that manage to-one relation. * @@ -36,7 +36,7 @@ protected AbstractToOneMapper(ServiceRegistry serviceRegistry, PropertyData prop @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -68,7 +68,7 @@ public Object mapToEntityFromMap( @Override public List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/BasicCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/BasicCollectionMapper.java index f1ab09f77507..dfcd05a2ff7c 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/BasicCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/BasicCollectionMapper.java @@ -14,7 +14,7 @@ import java.util.Set; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; @@ -86,7 +86,7 @@ else if ( oldCollection instanceof Map ) { @Override protected void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object changed) { @@ -108,7 +108,7 @@ protected Set buildCollectionChangeSet(Object eventCollection, Collectio @Override protected List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, PersistentCollection newColl, Serializable oldColl, Object id) { @@ -154,7 +154,7 @@ protected List mapCollectionChanges( } private boolean isCollectionElementSame( - SessionImplementor session, + SharedSessionContractImplementor session, CollectionPersister collectionPersister, Object lhs, Object rhs) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ListCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ListCollectionMapper.java index 05c5234016c4..e65c9fc9b67b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ListCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ListCollectionMapper.java @@ -14,7 +14,7 @@ import java.util.Set; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; @@ -91,7 +91,7 @@ protected Collection getOldCollectionContent(Serializable oldCollection) { @Override @SuppressWarnings("unchecked") protected void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object changed) { @@ -124,7 +124,7 @@ protected Set buildCollectionChangeSet(Object eventCollection, Collectio @Override protected List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, PersistentCollection newColl, Serializable oldColl, Object id) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MapCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MapCollectionMapper.java index d8fa3036670e..b7a3c0a8790d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MapCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MapCollectionMapper.java @@ -14,7 +14,7 @@ import java.util.Set; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; @@ -88,7 +88,7 @@ protected Collection getOldCollectionContent(Serializable oldCollection) { @Override protected void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object changed) { @@ -137,7 +137,7 @@ protected boolean isSame(CollectionPersister collectionPersister, Object oldObje @Override public List mapCollectionChanges( - SessionImplementor session, + SharedSessionContractImplementor session, PersistentCollection newColl, Serializable oldColl, Object id) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MiddleMapKeyEnumeratedComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MiddleMapKeyEnumeratedComponentMapper.java index 21da1f710762..8ec406659ef2 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MiddleMapKeyEnumeratedComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/MiddleMapKeyEnumeratedComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleComponentMapper; import org.hibernate.envers.internal.tools.query.Parameters; @@ -34,7 +34,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java index 64bbf686c1ef..63fc88456c25 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java @@ -4,11 +4,7 @@ */ package org.hibernate.envers.internal.entities.mapper.relation; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.PropertyData; @@ -19,6 +15,10 @@ import org.hibernate.persister.entity.EntityPersister; import org.jboss.logging.Logger; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + /** * @author Adam Warski (adam at warski dot org) * @author HernпїЅn Chanfreau @@ -48,7 +48,7 @@ public ToOneIdMapper( @Override public boolean mapToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -70,7 +70,7 @@ public boolean mapToMapFromEntity( @Override public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, + SharedSessionContractImplementor session, Map data, Object newObj, Object oldObj) { @@ -89,7 +89,7 @@ public void mapModifiedFlagsToMapForCollectionChange(String collectionPropertyNa } } - protected boolean checkModified(SessionImplementor session, Object newObj, Object oldObj) { + protected boolean checkModified(SharedSessionContractImplementor session, Object newObj, Object oldObj) { if ( nonInsertableFake ) { return false; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleComponentMapper.java index 9d78b4adc68f..5a440b4e937b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleComponentMapper.java @@ -4,12 +4,12 @@ */ package org.hibernate.envers.internal.entities.mapper.relation.component; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) */ @@ -38,7 +38,7 @@ Object mapToObjectFromFullMap( * @param obj Object to map from. */ void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleDummyComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleDummyComponentMapper.java index e069783634a5..ad1dad7ef021 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleDummyComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleDummyComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; @@ -22,7 +22,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java index f072501dbc5c..c9493f6a7c8d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.entities.PropertyData; @@ -66,7 +66,7 @@ private Object getComponentInstance(Object dataObject) { @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapElementNotKeyComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapElementNotKeyComponentMapper.java index 250e488736ca..19eb6a6fcec6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapElementNotKeyComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapElementNotKeyComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; @@ -45,7 +45,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java index d88672ddc614..839496b0a65e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.entities.mapper.id.IdMapper; @@ -38,7 +38,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java index 70913d8a0535..594ceeeb793e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; @@ -43,7 +43,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleRelatedComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleRelatedComponentMapper.java index d80015d23e59..082af9daf243 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleRelatedComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleRelatedComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.tools.query.Parameters; @@ -30,7 +30,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleSimpleComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleSimpleComponentMapper.java index d89c202dea35..9d9263957858 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleSimpleComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleSimpleComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; @@ -34,7 +34,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleStraightComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleStraightComponentMapper.java index c9da7aa0636c..0bc73de0a103 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleStraightComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/component/MiddleStraightComponentMapper.java @@ -6,7 +6,7 @@ import java.util.Map; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.entities.EntityInstantiator; import org.hibernate.envers.internal.tools.query.Parameters; @@ -33,7 +33,7 @@ public Object mapToObjectFromFullMap( @Override public void mapToMapFromObject( - SessionImplementor session, + SharedSessionContractImplementor session, Map idData, Map data, Object obj) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/reader/AuditReaderImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/reader/AuditReaderImpl.java index 2c93fa5c4fa2..2d106e9c5020 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/reader/AuditReaderImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/reader/AuditReaderImpl.java @@ -339,7 +339,7 @@ public T getCurrentRevision(Class revisionEntityClass, boolean persist) { final AuditProcess auditProcess = enversService.getAuditProcessManager().get( (EventSource) session ); // And getting the current revision data - return (T) auditProcess.getCurrentRevisionData( session, persist ); + return (T) auditProcess.getCurrentRevisionData( (SessionImplementor) session, persist ); } @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java index 5f7e364f8236..28d1d810b3e5 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java @@ -6,12 +6,14 @@ import java.lang.reflect.Constructor; -import org.hibernate.Session; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.EntityTrackingRevisionListener; import org.hibernate.envers.RevisionListener; import org.hibernate.envers.RevisionType; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.synchronization.SessionCacheCleaner; +import org.hibernate.envers.internal.tools.OrmTools; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.internal.Helper; @@ -56,12 +58,14 @@ public void setRevisionInfoNumberReader(RevisionInfoNumberReader revisionInfoNum } @Override - public void saveRevisionData(Session session, Object revisionData) { - session.persist( revisionInfoEntityName, revisionData ); + public void saveRevisionData(SharedSessionContractImplementor session, Object revisionData) { + OrmTools.saveData( revisionInfoEntityName, revisionData, session ); if ( revisionInfoNumberReader != null && revisionInfoNumberReader.getRevisionNumber( revisionData ).longValue() < 0 ) { throw new AuditException( "Negative revision numbers are not allowed" ); } - sessionCacheCleaner.scheduleAuditDataRemoval( session, revisionData ); + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, revisionData ); + } } @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/RevisionInfoGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/RevisionInfoGenerator.java index f5b962a4d616..27c7d631aa49 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/RevisionInfoGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/RevisionInfoGenerator.java @@ -4,7 +4,7 @@ */ package org.hibernate.envers.internal.revisioninfo; -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; /** @@ -16,7 +16,7 @@ public interface RevisionInfoGenerator { */ void setRevisionInfoNumberReader(RevisionInfoNumberReader revisionInfoNumberReader); - void saveRevisionData(Session session, Object revisionData); + void saveRevisionData(SharedSessionContractImplementor session, Object revisionData); Object generate(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcess.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcess.java index b228ac366bce..724ee86256d3 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcess.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcess.java @@ -10,9 +10,10 @@ import java.util.Queue; import org.hibernate.FlushMode; -import org.hibernate.Session; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.StatelessSessionImplementor; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.revisioninfo.RevisionInfoGenerator; import org.hibernate.envers.internal.synchronization.work.AuditWorkUnit; @@ -28,16 +29,18 @@ public class AuditProcess implements BeforeTransactionCompletionProcess { private static final Logger log = Logger.getLogger( AuditProcess.class ); private final RevisionInfoGenerator revisionInfoGenerator; - private final SessionImplementor session; + private final SharedSessionContractImplementor session; private final LinkedList workUnits; private final Queue undoQueue; private final Map, AuditWorkUnit> usedIds; private final Map, Object[]> entityStateCache; private final EntityChangeNotifier entityChangeNotifier; + private Object revisionData; + private boolean revisionDataSaved; - public AuditProcess(RevisionInfoGenerator revisionInfoGenerator, SessionImplementor session) { + public AuditProcess(RevisionInfoGenerator revisionInfoGenerator, SharedSessionContractImplementor session) { this.revisionInfoGenerator = revisionInfoGenerator; this.session = session; @@ -108,40 +111,31 @@ public void addWorkUnit(AuditWorkUnit vwu) { } } - private void executeInSession(Session session) { - // Making sure the revision data is persisted. - final Object currentRevisionData = getCurrentRevisionData( session, true ); - - AuditWorkUnit vwu; - - // First undoing any performed work units - while ( (vwu = undoQueue.poll()) != null ) { - vwu.undo( session ); - } - - while ( (vwu = workUnits.poll()) != null ) { - vwu.perform( session, revisionData ); - entityChangeNotifier.entityChanged( session, currentRevisionData, vwu ); - } - } - - public Object getCurrentRevisionData(Session session, boolean persist) { + public Object getCurrentRevisionData(SharedSessionContractImplementor session, boolean persist) { // Generating the revision data if not yet generated if ( revisionData == null ) { revisionData = revisionInfoGenerator.generate(); } // Saving the revision data, if not yet saved and persist is true - if ( !session.contains( revisionData ) && persist ) { - revisionInfoGenerator.saveRevisionData( session, revisionData ); + if ( session instanceof SessionImplementor statefulSession ) { + if ( persist && !statefulSession.contains( revisionData ) ) { + revisionInfoGenerator.saveRevisionData( session, revisionData ); + } + } + else if ( session instanceof StatelessSessionImplementor statelessSession ) { + if ( persist && !revisionDataSaved ) { + revisionInfoGenerator.saveRevisionData( session, revisionData ); + revisionDataSaved = true; + } } return revisionData; } @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { - if ( workUnits.size() == 0 && undoQueue.size() == 0 ) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { + if ( workUnits.isEmpty() && undoQueue.isEmpty() ) { return; } @@ -150,30 +144,71 @@ public void doBeforeTransactionCompletion(SessionImplementor session) { return; } - // see: http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4178431 - if ( FlushMode.MANUAL.equals( session.getHibernateFlushMode() ) || session.isClosed() ) { - Session temporarySession = null; - try { - temporarySession = session.sessionWithOptions() + if ( session instanceof StatelessSessionImplementor statelessSession ) { + if ( statelessSession.isClosed() ) { + try (StatelessSessionImplementor temporarySession = (StatelessSessionImplementor) statelessSession.statelessWithOptions() .connection() - .autoClose( false ) - .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION ) .noInterceptor() - .openSession(); + .open()) { + executeInStatelessSession( temporarySession ); + } + } + else { + executeInStatelessSession( statelessSession ); + } + } + else if ( FlushMode.MANUAL.equals( session.getHibernateFlushMode() ) || session.isClosed() ) { + assert session instanceof SessionImplementor; + final SessionImplementor statefulSession = (SessionImplementor) session; + try (SessionImplementor temporarySession = (SessionImplementor) statefulSession.sessionWithOptions() + .connection() + .autoClose( false ) + .connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION ) + .noInterceptor() + .openSession()) { executeInSession( temporarySession ); temporarySession.flush(); } - finally { - if ( temporarySession != null ) { - temporarySession.close(); - } - } } else { - executeInSession( session ); + executeInSession( (SessionImplementor) session ); // Explicitly flushing the session, as the auto-flush may have already happened. session.flush(); } } + + private void executeInSession(SessionImplementor statefulSession) { + // Making sure the revision data is persisted. + final Object currentRevisionData = getCurrentRevisionData( statefulSession, true ); + + AuditWorkUnit vwu; + + // First undoing any performed work units + while ( (vwu = undoQueue.poll()) != null ) { + vwu.undo( statefulSession ); + } + + while ( (vwu = workUnits.poll()) != null ) { + vwu.perform( statefulSession, revisionData ); + entityChangeNotifier.entityChanged( statefulSession, currentRevisionData, vwu ); + } + } + + private void executeInStatelessSession(StatelessSessionImplementor statelessSession) { + // Making sure the revision data is persisted. + final Object currentRevisionData = getCurrentRevisionData( statelessSession, true ); + + AuditWorkUnit vwu; + + // First undoing any performed work units + while ( (vwu = undoQueue.poll()) != null ) { + vwu.undo( statelessSession ); + } + + while ( (vwu = workUnits.poll()) != null ) { + vwu.perform( statelessSession, revisionData ); + entityChangeNotifier.entityChanged( statelessSession, currentRevisionData, vwu ); + } + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcessManager.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcessManager.java index f719c61aafc3..2c8cf8d350c5 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcessManager.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/AuditProcessManager.java @@ -10,10 +10,9 @@ import org.hibernate.Transaction; import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.TransactionCompletionCallbacks; import org.hibernate.envers.internal.revisioninfo.RevisionInfoGenerator; -import org.hibernate.event.spi.EventSource; /** * @author Adam Warski (adam at warski dot org) @@ -28,7 +27,7 @@ public AuditProcessManager(RevisionInfoGenerator revisionInfoGenerator) { this.revisionInfoGenerator = revisionInfoGenerator; } - public AuditProcess get(EventSource session) { + public AuditProcess get(SharedSessionContractImplementor session) { final Transaction transaction = session.accessTransaction(); AuditProcess auditProcess = auditProcesses.get( transaction ); @@ -37,24 +36,21 @@ public AuditProcess get(EventSource session) { auditProcess = new AuditProcess( revisionInfoGenerator, session ); auditProcesses.put( transaction, auditProcess ); - session.getActionQueue().registerProcess( - new BeforeTransactionCompletionProcess() { - public void doBeforeTransactionCompletion(SessionImplementor session) { - final AuditProcess process = auditProcesses.get( transaction ); - if ( process != null ) { - process.doBeforeTransactionCompletion( session ); - } - } + final TransactionCompletionCallbacks transactionCompletionCallbacks = session.getTransactionCompletionCallbacks(); + transactionCompletionCallbacks.registerCallback( new BeforeTransactionCompletionProcess() { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { + final AuditProcess process = auditProcesses.get( transaction ); + if ( process != null ) { + process.doBeforeTransactionCompletion( session ); } - ); - - session.getActionQueue().registerProcess( - new AfterTransactionCompletionProcess() { - public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { - auditProcesses.remove( transaction ); - } - } - ); + } + } ); + + transactionCompletionCallbacks.registerCallback( new AfterTransactionCompletionProcess() { + public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { + auditProcesses.remove( transaction ); + } + } ); } return auditProcess; diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/EntityChangeNotifier.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/EntityChangeNotifier.java index d6d9ae9aabfb..a87244d31391 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/EntityChangeNotifier.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/EntityChangeNotifier.java @@ -4,8 +4,7 @@ */ package org.hibernate.envers.internal.synchronization; -import org.hibernate.Session; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.revisioninfo.RevisionInfoGenerator; import org.hibernate.envers.internal.synchronization.work.AuditWorkUnit; import org.hibernate.envers.internal.synchronization.work.PersistentCollectionChangeWorkUnit; @@ -18,9 +17,9 @@ */ public class EntityChangeNotifier { private final RevisionInfoGenerator revisionInfoGenerator; - private final SessionImplementor sessionImplementor; + private final SharedSessionContractImplementor sessionImplementor; - public EntityChangeNotifier(RevisionInfoGenerator revisionInfoGenerator, SessionImplementor sessionImplementor) { + public EntityChangeNotifier(RevisionInfoGenerator revisionInfoGenerator, SharedSessionContractImplementor sessionImplementor) { this.revisionInfoGenerator = revisionInfoGenerator; this.sessionImplementor = sessionImplementor; } @@ -34,7 +33,7 @@ public EntityChangeNotifier(RevisionInfoGenerator revisionInfoGenerator, Session * @param currentRevisionData Revision log entity. * @param vwu Performed work unit. */ - public void entityChanged(Session session, Object currentRevisionData, AuditWorkUnit vwu) { + public void entityChanged(SharedSessionContractImplementor session, Object currentRevisionData, AuditWorkUnit vwu) { Object entityId = vwu.getEntityId(); if ( entityId instanceof PersistentCollectionChangeWorkUnit.PersistentCollectionChangeWorkUnitId ) { // Notify about a change in collection owner entity. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/SessionCacheCleaner.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/SessionCacheCleaner.java index cdcfc768feb3..cb85281722b0 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/SessionCacheCleaner.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/SessionCacheCleaner.java @@ -25,7 +25,7 @@ public class SessionCacheCleaner { * @param data Audit data that shall be evicted (e.g. revision data or entity snapshot) */ public void scheduleAuditDataRemoval(final Session session, final Object data) { - ( (EventSource) session ).getActionQueue().registerProcess( + ( (EventSource) session ).getActionQueue().registerCallback( new AfterTransactionCompletionProcess() { public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor sessionImplementor) { if ( !sessionImplementor.isClosed() ) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AbstractAuditWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AbstractAuditWorkUnit.java index 9afbd896c00d..4a0dd67dcc1b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AbstractAuditWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AbstractAuditWorkUnit.java @@ -4,16 +4,16 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.Session; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.mapper.id.IdMapper; -import org.hibernate.envers.strategy.AuditStrategy; +import org.hibernate.envers.internal.tools.OrmTools; +import org.hibernate.envers.strategy.spi.AuditStrategy; + +import java.util.HashMap; +import java.util.Map; /** * @author Adam Warski (adam at warski dot org) @@ -22,7 +22,7 @@ * @author Chris Cranford */ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit { - protected final SessionImplementor sessionImplementor; + protected final SharedSessionContractImplementor sessionImplementor; protected final EnversService enversService; protected final Object id; protected final String entityName; @@ -32,7 +32,7 @@ public abstract class AbstractAuditWorkUnit implements AuditWorkUnit { private Object performedData; protected AbstractAuditWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, @@ -61,10 +61,10 @@ protected void fillDataWithId(Map data, Object revision) { } @Override - public void perform(Session session, Object revisionData) { + public void perform(SharedSessionContractImplementor session, Object revisionData) { final Map data = generateData( revisionData ); - auditStrategy.perform( session, getEntityName(), enversService, id, data, revisionData ); + auditStrategy.perform( session, getEntityName(), enversService.getConfig(), id, data, revisionData ); setPerformed( data ); } @@ -88,9 +88,9 @@ protected void setPerformed(Object performedData) { this.performedData = performedData; } - public void undo(Session session) { + public void undo(SharedSessionContractImplementor session) { if ( isPerformed() ) { - session.remove( performedData ); + OrmTools.removeData( performedData, session ); session.flush(); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AddWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AddWorkUnit.java index 9e1452ee6a5f..31e2e44e991b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AddWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AddWorkUnit.java @@ -4,10 +4,7 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.PropertyData; @@ -15,6 +12,9 @@ import org.hibernate.envers.internal.tools.ArraysTools; import org.hibernate.persister.entity.EntityPersister; +import java.util.HashMap; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) @@ -24,7 +24,7 @@ public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit private final Map data; public AddWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, EntityPersister entityPersister, Object[] state) { @@ -42,7 +42,7 @@ public AddWorkUnit( } public AddWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AuditWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AuditWorkUnit.java index 2e72d6b1d824..c91743182a38 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AuditWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/AuditWorkUnit.java @@ -6,11 +6,11 @@ import java.util.Map; -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; /** - * TODO: refactor constructors into factory methods + * Captures specific auditable mutation events. * * @author Adam Warski (adam at warski dot org) */ @@ -30,9 +30,9 @@ public interface AuditWorkUnit extends WorkUnitMergeVisitor, WorkUnitMergeDispat * @param revisionData The current revision data, which will be used to populate the work unit with the correct * revision relation. */ - void perform(Session session, Object revisionData); + void perform(SharedSessionContractImplementor session, Object revisionData); - void undo(Session session); + void undo(SharedSessionContractImplementor session); /** * @param revisionData The current revision data, which will be used to populate the work unit with the correct diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/CollectionChangeWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/CollectionChangeWorkUnit.java index 7a795c7c7eef..7d37beb5a872 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/CollectionChangeWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/CollectionChangeWorkUnit.java @@ -4,15 +4,15 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.EntityConfiguration; import org.hibernate.envers.internal.entities.mapper.PropertyMapper; +import java.util.HashMap; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Michal Skowronek (mskowr at o2 dot pl) @@ -23,7 +23,7 @@ public class CollectionChangeWorkUnit extends AbstractAuditWorkUnit implements A private final Map data = new HashMap<>(); public CollectionChangeWorkUnit( - SessionImplementor session, + SharedSessionContractImplementor session, String entityName, String collectionPropertyName, EnversService enversService, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/DelWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/DelWorkUnit.java index b053f5b0b28a..f10e0db61430 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/DelWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/DelWorkUnit.java @@ -4,15 +4,15 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.tools.ArraysTools; import org.hibernate.persister.entity.EntityPersister; +import java.util.HashMap; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) @@ -24,7 +24,7 @@ public class DelWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit private final String[] propertyNames; public DelWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/FakeBidirectionalRelationWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/FakeBidirectionalRelationWorkUnit.java index 1f9fa953221d..5e79626d135d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/FakeBidirectionalRelationWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/FakeBidirectionalRelationWorkUnit.java @@ -11,6 +11,7 @@ import java.util.Set; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.entities.RelationDescription; @@ -181,7 +182,7 @@ public RevisionType getRevisionType() { return revisionType; } - public void generateData(SessionImplementor sessionImplementor, Map data) { + public void generateData(SharedSessionContractImplementor sessionImplementor, Map data) { // If the revision type is "DEL", it means that the object is removed from the collection. Then the // new owner will in fact be null. rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/ModWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/ModWorkUnit.java index b10ebb48a9bd..f3f0d96c5b78 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/ModWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/ModWorkUnit.java @@ -4,14 +4,14 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.persister.entity.EntityPersister; +import java.util.HashMap; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) */ @@ -24,7 +24,7 @@ public class ModWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit private final Object[] newState; public ModWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/PersistentCollectionChangeWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/PersistentCollectionChangeWorkUnit.java index 3dd741c4d948..48dda8332df7 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/PersistentCollectionChangeWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/synchronization/work/PersistentCollectionChangeWorkUnit.java @@ -4,21 +4,20 @@ */ package org.hibernate.envers.internal.synchronization.work; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.hibernate.Session; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionEntry; -import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author Adam Warski (adam at warski dot org) * @author Chris Cranford @@ -28,7 +27,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im private final String referencingPropertyName; public PersistentCollectionChangeWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, PersistentCollection collection, @@ -51,7 +50,7 @@ public PersistentCollectionChangeWorkUnit( } public PersistentCollectionChangeWorkUnit( - SessionImplementor sessionImplementor, + SharedSessionContractImplementor sessionImplementor, String entityName, EnversService enversService, Object id, @@ -75,7 +74,7 @@ public Map generateData(Object revisionData) { @Override @SuppressWarnings("unchecked") - public void perform(Session session, Object revisionData) { + public void perform(SharedSessionContractImplementor session, Object revisionData) { final Configuration configuration = enversService.getConfig(); for ( PersistentCollectionChangeData persistentCollectionChangeData : collectionChanges ) { @@ -87,7 +86,7 @@ public void perform(Session session, Object revisionData) { session, getEntityName(), referencingPropertyName, - enversService, + enversService.getConfig(), persistentCollectionChangeData, revisionData ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/EntityTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/EntityTools.java index 39fb39032bba..43bdfb59d45e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/EntityTools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/EntityTools.java @@ -4,29 +4,28 @@ */ package org.hibernate.envers.internal.tools; -import java.util.Objects; - import org.hibernate.Session; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; +import java.util.Objects; + /** * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ public abstract class EntityTools { - public static boolean entitiesEqual(SessionImplementor session, String entityName, Object obj1, Object obj2) { + public static boolean entitiesEqual(SharedSessionContractImplementor session, String entityName, Object obj1, Object obj2) { final Object id1 = getIdentifier( session, entityName, obj1 ); final Object id2 = getIdentifier( session, entityName, obj2 ); return Objects.deepEquals( id1, id2 ); } - public static Object getIdentifier(SessionImplementor session, String entityName, Object obj) { + public static Object getIdentifier(SharedSessionContractImplementor session, String entityName, Object obj) { if ( obj == null ) { return null; } @@ -86,7 +85,7 @@ else if ( HibernateProxy.class.isAssignableFrom( clazz ) ) { /** * @return Java class mapped to specified entity name. */ - public static Class getEntityClass(SessionImplementor sessionImplementor, String entityName) { + public static Class getEntityClass(SharedSessionContractImplementor sessionImplementor, String entityName) { final EntityPersister entityPersister = sessionImplementor.getFactory() .getMappingMetamodel() .getEntityDescriptor( entityName ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/OrmTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/OrmTools.java new file mode 100644 index 000000000000..ac8ed3ad8ef3 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/OrmTools.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.envers.internal.tools; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.StatelessSessionImplementor; + +import java.util.Locale; + +/** + * Tools for dealing with Hibernate ORM, especially in regard to handling differences + * between {@linkplain org.hibernate.Session stateful} and {@linkplain StatelessSession stateless} + * sessions. + * + * @author Steve Ebersole + */ +public class OrmTools { + public static Object loadAuditEntity(String entityName, Object id, SharedSessionContractImplementor session) { + if ( session instanceof SessionImplementor statefulSession ) { + return statefulSession.getReference( entityName, id ); + } + else if ( session instanceof StatelessSession statelessSession ) { + return statelessSession.get( entityName, id ); + } + else { + throw unexpectedSessionType( session ); + } + } + + public static void saveData( + String auditEntityName, + Object data, + SharedSessionContractImplementor session) { + if ( session instanceof SessionImplementor statefulSession ) { + statefulSession.persist( auditEntityName, data ); + } + else if ( session instanceof StatelessSession statelessSession ) { + statelessSession.insert( auditEntityName, data ); + } + else { + throw unexpectedSessionType( session ); + } + } + + public static void removeData(Object data, SharedSessionContractImplementor session) { + if ( session instanceof SessionImplementor statefulSession ) { + statefulSession.remove( data ); + } + else if ( session instanceof StatelessSessionImplementor statelessSession ) { + statelessSession.delete( data ); + } + else { + unexpectedSessionType( session ); + } + } + + private static UnsupportedOperationException unexpectedSessionType(SharedSessionContractImplementor session) { + throw new UnsupportedOperationException( String.format( + Locale.ROOT, + "Unexpected argument type (`%s`); expecting `%s` or `%s`", + session.getClass().getName(), + Session.class.getName(), + StatelessSession.class.getName() + ) ); + + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java index 63797c107f8b..9b52cf565a59 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/query/QueryBuilder.java @@ -14,8 +14,8 @@ import jakarta.persistence.criteria.JoinType; import org.hibernate.HibernateException; -import org.hibernate.Session; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; @@ -400,7 +400,7 @@ private List getOrderList() { return orderList; } - public Query toQuery(Session session) { + public Query toQuery(SharedSessionContractImplementor session) { final StringBuilder querySb = new StringBuilder(); final Map queryParamValues = new HashMap<>(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java index 7290e4a6ebc5..0490b4896e4b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/RevisionsOfEntityQuery.java @@ -164,7 +164,7 @@ ORDER BY e.revision ASC (unless another order or projection is specified) } if ( !selectEntitiesOnly ) { - qb.addFrom( configuration.getRevisionInfo().getRevisionInfoEntityName(), "r", true ); + qb.addFrom( configuration.getRevisionInfo().getRevisionInfoQueryName(), "r", true ); qb.getRootParameters().addWhere( configuration.getRevisionNumberPath(), true, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java index 4cd3f0ac33fd..cff9fa1716ba 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java @@ -5,6 +5,7 @@ package org.hibernate.envers.strategy; import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData; @@ -29,7 +30,7 @@ public interface AuditStrategy extends org.hibernate.envers.strategy.spi.AuditSt * @param id Id of the entity. * @param data Audit data to persist * @param revision Current revision data - * @deprecated use {@link org.hibernate.envers.strategy.spi.AuditStrategy#perform(Session, String, Configuration, Object, Object, Object)} + * @deprecated use {@link org.hibernate.envers.strategy.spi.AuditStrategy#perform(org.hibernate.engine.spi.SharedSessionContractImplementor, String, Configuration, Object, Object, Object)} */ @Deprecated(since = "5.2.1") default void perform( @@ -40,7 +41,7 @@ default void perform( Object data, Object revision) { perform( - session, + (SharedSessionContractImplementor) session, entityName, enversService.getConfig(), id, @@ -59,7 +60,7 @@ default void perform( * @param enversService The EnversService * @param persistentCollectionChangeData Collection change data to be persisted. * @param revision Current revision data - * @deprecated use {@link #performCollectionChange(Session, String, String, Configuration, PersistentCollectionChangeData, Object)} + * @deprecated use {@link #performCollectionChange(SharedSessionContractImplementor, String, String, Configuration, PersistentCollectionChangeData, Object)} */ @Deprecated(since = "5.2.1") default void performCollectionChange( @@ -70,7 +71,7 @@ default void performCollectionChange( PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { performCollectionChange( - session, + (SharedSessionContractImplementor) session, entityName, propertyName, enversService.getConfig(), diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/DefaultAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/DefaultAuditStrategy.java index 1974bd81e488..984f437c2af4 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/DefaultAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/DefaultAuditStrategy.java @@ -4,12 +4,14 @@ */ package org.hibernate.envers.strategy.internal; -import org.hibernate.Session; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.synchronization.SessionCacheCleaner; +import org.hibernate.envers.internal.tools.OrmTools; import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.strategy.AuditStrategy; @@ -35,26 +37,35 @@ public DefaultAuditStrategy() { @Override public void perform( - Session session, + SharedSessionContractImplementor session, String entityName, Configuration configuration, Object id, Object data, Object revision) { - session.persist( configuration.getAuditEntityName( entityName ), data ); - sessionCacheCleaner.scheduleAuditDataRemoval( session, data ); + OrmTools.saveData( configuration.getAuditEntityName( entityName ), data, session ); + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, data ); + } } @Override public void performCollectionChange( - Session session, + SharedSessionContractImplementor session, String entityName, String propertyName, Configuration configuration, PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { - session.persist( persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData() ); - sessionCacheCleaner.scheduleAuditDataRemoval( session, persistentCollectionChangeData.getData() ); + OrmTools.saveData( + persistentCollectionChangeData.getEntityName(), + persistentCollectionChangeData.getData(), + session + ); + + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, persistentCollectionChangeData.getData() ); + } } /** diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java index 2d88fe08f0aa..f79794cfe2da 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java @@ -4,18 +4,8 @@ */ package org.hibernate.envers.strategy.internal; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; - import jakarta.persistence.LockModeType; import org.hibernate.FlushMode; -import org.hibernate.Session; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -32,16 +22,16 @@ import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.internal.revisioninfo.RevisionInfoNumberReader; import org.hibernate.envers.internal.synchronization.SessionCacheCleaner; +import org.hibernate.envers.internal.tools.OrmTools; import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.envers.internal.tools.query.QueryBuilder; import org.hibernate.envers.strategy.AuditStrategy; import org.hibernate.envers.strategy.spi.AuditStrategyContext; import org.hibernate.envers.strategy.spi.MappingContext; -import org.hibernate.event.spi.EventSource; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.persister.entity.JoinedSubclassEntityPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.JoinedSubclassEntityPersister; import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.property.access.spi.Getter; import org.hibernate.sql.ComparisonRestriction; @@ -53,6 +43,15 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.Type; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; + import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS; import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER; @@ -152,16 +151,14 @@ public void addAdditionalColumns(MappingContext mappingContext) { @Override public void perform( - final Session session, + final SharedSessionContractImplementor session, final String entityName, final Configuration configuration, final Object id, final Object data, final Object revision) { final String auditedEntityName = configuration.getAuditEntityName( entityName ); - - // Save the audit data - session.persist( auditedEntityName, data ); + OrmTools.saveData( auditedEntityName, data, session ); // Update the end date of the previous row. // @@ -173,12 +170,12 @@ public void perform( final boolean reuseEntityIdentifier = configuration.isAllowIdentifierReuse(); if ( reuseEntityIdentifier || getRevisionType( configuration, data ) != RevisionType.ADD ) { // Register transaction completion process to guarantee execution of UPDATE statement after INSERT. - ( (EventSource) session ).getActionQueue().registerProcess( sessionImplementor -> { + session.getTransactionCompletionCallbacks().registerCallback( (s) -> { // Construct the update contexts final List contexts = getUpdateContexts( entityName, auditedEntityName, - sessionImplementor, + session, configuration, id, revision @@ -196,7 +193,7 @@ public void perform( } for ( UpdateContext context : contexts ) { - final int rows = executeUpdate( sessionImplementor, context ); + final int rows = executeUpdate( session, context ); if ( rows != 1 ) { final RevisionType revisionType = getRevisionType( configuration, data ); if ( !reuseEntityIdentifier || revisionType != RevisionType.ADD ) { @@ -214,27 +211,30 @@ public void perform( } } ); } - sessionCacheCleaner.scheduleAuditDataRemoval( session, data ); + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, data ); + } } @Override @SuppressWarnings("unchecked") public void performCollectionChange( - Session session, + SharedSessionContractImplementor session, String entityName, String propertyName, Configuration configuration, - PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { + PersistentCollectionChangeData persistentCollectionChangeData, + Object revision) { final QueryBuilder qb = new QueryBuilder( persistentCollectionChangeData.getEntityName(), MIDDLE_ENTITY_ALIAS, - ( (SharedSessionContractImplementor) session ).getFactory() + session.getFactory() ); final String originalIdPropName = configuration.getOriginalIdPropertyName(); - final Map originalId = (Map) persistentCollectionChangeData.getData().get( - originalIdPropName - ); + final Map originalId = (Map) persistentCollectionChangeData + .getData() + .get( originalIdPropName ); final String revisionFieldName = configuration.getRevisionFieldName(); final String revisionTypePropName = configuration.getRevisionTypePropertyName(); final String ordinalPropName = configuration.getEmbeddableSetOrdinalPropertyName(); @@ -251,7 +251,7 @@ public void performCollectionChange( } } - if ( isNonIdentifierWhereConditionsRequired( entityName, propertyName, (SessionImplementor) session ) ) { + if ( isNonIdentifierWhereConditionsRequired( entityName, propertyName, session ) ) { addNonIdentifierWhereConditions( qb, persistentCollectionChangeData.getData(), originalIdPropName ); } @@ -265,15 +265,22 @@ public void performCollectionChange( // Update the last revision if one exists. // HHH-5967: with collections, the same element can be added and removed multiple times. So even if it's an // ADD, we may need to update the last revision. - if ( l.size() > 0 ) { + if ( !l.isEmpty() ) { updateLastRevision( - session, configuration, l, originalId, persistentCollectionChangeData.getEntityName(), revision + session, + configuration, + l, + originalId, + persistentCollectionChangeData.getEntityName(), + revision ); } // Save the audit data - session.persist( persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData() ); - sessionCacheCleaner.scheduleAuditDataRemoval( session, persistentCollectionChangeData.getData() ); + OrmTools.saveData( persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData(), session ); + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, persistentCollectionChangeData.getData() ); + } } /** @@ -352,7 +359,7 @@ private RevisionType getRevisionType(Configuration configuration, Object data) { @SuppressWarnings("unchecked") private void updateLastRevision( - Session session, + SharedSessionContractImplementor session, Configuration configuration, List l, Object id, @@ -376,8 +383,10 @@ private void updateLastRevision( } // Saving the previous version - session.persist( auditedEntityName, previousData ); - sessionCacheCleaner.scheduleAuditDataRemoval( session, previousData ); + OrmTools.saveData( auditedEntityName, previousData, session ); + if ( session instanceof SessionImplementor statefulSession ) { + sessionCacheCleaner.scheduleAuditDataRemoval( statefulSession, previousData ); + } } else { throw new RuntimeException( "Cannot find previous revision for entity " + auditedEntityName + " and id " + id ); @@ -406,7 +415,7 @@ private Object getRevEndTimestampValue(Configuration configuration, Object value return convertRevEndTimestampToDate( value ); } - private EntityPersister getEntityPersister(String entityName, SessionImplementor sessionImplementor) { + private EntityPersister getEntityPersister(String entityName, SharedSessionContractImplementor sessionImplementor) { return sessionImplementor.getFactory() .getMappingMetamodel() .getEntityDescriptor( entityName ); @@ -430,7 +439,10 @@ private void addNonIdentifierWhereConditions(QueryBuilder qb, Map getUpdateContexts( String entityName, String auditEntityName, - SessionImplementor session, + SharedSessionContractImplementor session, Configuration configuration, Object id, Object revision) { @@ -506,16 +518,14 @@ private List getUpdateContexts( if ( entity instanceof JoinedSubclassEntityPersister ) { // iterate subclasses, excluding root while ( entity.getMappedSuperclass() != null ) { - contexts.add( - getNonRootUpdateContext( - entityName, - auditEntityName, - session, - configuration, - id, - revision - ) - ); + contexts.add( getNonRootUpdateContext( + entityName, + auditEntityName, + session, + configuration, + id, + revision + ) ); entityName = entity.getEntityMappingType().getSuperMappingType().getEntityName(); auditEntityName = configuration.getAuditEntityName( entityName ); entity = getEntityPersister( entityName, session ); @@ -524,16 +534,14 @@ private List getUpdateContexts( } // add root - contexts.add( - getUpdateContext( - entityName, - auditEntityName, - session, - configuration, - id, - revision - ) - ); + contexts.add( getUpdateContext( + entityName, + auditEntityName, + session, + configuration, + id, + revision + ) ); return contexts; } @@ -541,7 +549,7 @@ private List getUpdateContexts( private UpdateContext getUpdateContext( String entityName, String auditEntityName, - SessionImplementor session, + SharedSessionContractImplementor session, Configuration configuration, Object id, Object revision) { @@ -607,7 +615,7 @@ private UpdateContext getUpdateContext( private UpdateContext getNonRootUpdateContext( String entityName, String auditEntityName, - SessionImplementor session, + SharedSessionContractImplementor session, Configuration configuration, Object id, Object revision) { @@ -684,7 +692,7 @@ public void bind(Object value, ModelPart part) { } private interface QueryParameterBinding { - int bind(int index, PreparedStatement statement, SessionImplementor session) throws SQLException; + int bind(int index, PreparedStatement statement, SharedSessionContractImplementor session) throws SQLException; } private static class QueryParameterBindingType implements QueryParameterBinding { @@ -696,7 +704,7 @@ public QueryParameterBindingType(Object value, Type type) { this.value = value; } - public int bind(int index, PreparedStatement statement, SessionImplementor session) throws SQLException { + public int bind(int index, PreparedStatement statement, SharedSessionContractImplementor session) throws SQLException { type.nullSafeSet( statement, value, index, session ); return type.getColumnSpan( session.getSessionFactory().getRuntimeMetamodels() ); } @@ -715,7 +723,7 @@ public QueryParameterBindingPart(Object value, ModelPart modelPart) { public int bind( int index, PreparedStatement statement, - SessionImplementor session) { + SharedSessionContractImplementor session) { try { return modelPart.breakDownJdbcValues( value, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/spi/AuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/spi/AuditStrategy.java index 31ade3fcfd06..ee70fce4e1bb 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/spi/AuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/spi/AuditStrategy.java @@ -5,7 +5,7 @@ package org.hibernate.envers.strategy.spi; import org.hibernate.Incubating; -import org.hibernate.Session; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData; import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData; @@ -59,7 +59,7 @@ default void postInitialize(AuditStrategyContext context) { * @param revision Current revision data. */ void perform( - Session session, + SharedSessionContractImplementor session, String entityName, Configuration configuration, Object id, @@ -78,7 +78,7 @@ void perform( * @param revision Current revision data */ void performCollectionChange( - Session session, + SharedSessionContractImplementor session, String entityName, String propertyName, Configuration configuration, diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/stateless/SimpleStatelessSessionTests.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/stateless/SimpleStatelessSessionTests.java new file mode 100644 index 000000000000..5cf60f683f82 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/stateless/SimpleStatelessSessionTests.java @@ -0,0 +1,185 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.envers.test.stateless; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Table; +import org.hibernate.envers.Audited; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@DomainModel(annotatedClasses = SimpleStatelessSessionTests.Person.class) +@SessionFactory +public class SimpleStatelessSessionTests { + + @Test + void simpleSession(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + final Person john = new Person( 1, "John" ); + session.persist( john ); + } ); + + checkCallbackCounts( 1, 0, 0 ); + + factoryScope.inTransaction( (session) -> { + final Long count = session.createQuery( "select count(*) from org.hibernate.envers.test.stateless.SimpleStatelessSessionTests$Person_AUD", Long.class ) + .getSingleResult(); + assertThat( count ).isEqualTo( 1 ); + } ); + + factoryScope.inTransaction( (session) -> { + final Person john = session.find( Person.class, 1 ); + john.name = "Jonathan"; + } ); + + checkCallbackCounts( 1, 1, 0 ); + + factoryScope.inTransaction( (session) -> { + final Person john = session.find( Person.class, 1 ); + session.remove( john ); + } ); + + checkCallbackCounts( 1, 1, 1 ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-19737" ) + void simpleStatelessSession(SessionFactoryScope factoryScope) { + factoryScope.inStatelessTransaction( (session) -> { + final Person john = new Person( 1, "John" ); + session.insert( john ); + } ); + + checkCallbackCounts( 1, 0, 0 ); + + factoryScope.inTransaction( (session) -> { + final Long count = session.createQuery( "select count(*) from org.hibernate.envers.test.stateless.SimpleStatelessSessionTests$Person_AUD", Long.class ) + .getSingleResult(); + assertThat( count ).isEqualTo( 1 ); + } ); + + factoryScope.inStatelessTransaction( (session) -> { + final Person john = session.get( Person.class, 1 ); + john.name = "Jonathan"; + session.update( john ); + } ); + + checkCallbackCounts( 1, 1, 0 ); + + factoryScope.inStatelessTransaction( (session) -> { + final Person john = session.get( Person.class, 1 ); + session.delete( john ); + } ); + + checkCallbackCounts( 1, 1, 1 ); + } + + private void checkCallbackCounts(int insert, int update, int delete) { + assertThat( Person.beforeInsertCalls ).isEqualTo( insert ); + assertThat( Person.afterInsertCalls ).isEqualTo( insert ); + + assertThat( Person.beforeUpdateCalls ).isEqualTo( update ); + assertThat( Person.afterUpdateCalls ).isEqualTo( update ); + + assertThat( Person.beforeDeleteCalls ).isEqualTo( delete ); + assertThat( Person.afterDeleteCalls ).isEqualTo( delete ); + + } + + @BeforeEach + void setUp() { + Person.resetCallbackState(); + } + + @AfterEach + void dropTestData(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + @Entity(name="Person") + @Table(name="persons") + @Audited + public static class Person { + public static int beforeInsertCalls; + public static int afterInsertCalls; + + public static int beforeUpdateCalls; + public static int afterUpdateCalls; + + public static int beforeDeleteCalls; + public static int afterDeleteCalls; + + @Id + private Integer id; + private String name; + + public Person() { + } + + public Person(Integer id, String name) { + this.id = id; + this.name = name; + } + + @PrePersist + public void beforeInsert() { + beforeInsertCalls++; + } + + @PostPersist + public void afterInsert() { + afterInsertCalls++; + } + + @PreUpdate + public void beforeUpdate() { + beforeUpdateCalls++; + } + + @PostUpdate + public void afterUpdate() { + afterUpdateCalls++; + } + + @PreRemove + public void beforeDelete() { + beforeDeleteCalls++; + } + + @PostRemove + public void afterDelete() { + afterDeleteCalls++; + } + + public static void resetCallbackState() { + beforeInsertCalls = 0; + afterInsertCalls = 0; + + beforeUpdateCalls = 0; + afterUpdateCalls = 0; + + beforeDeleteCalls = 0; + afterDeleteCalls = 0; + } + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/RegisterUserEventListenersTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/RegisterUserEventListenersTest.java index ea124946b8e5..70ca3cfb6f97 100644 --- a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/RegisterUserEventListenersTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/RegisterUserEventListenersTest.java @@ -7,7 +7,6 @@ import org.hibernate.Session; import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.internal.tools.MutableInteger; import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase; @@ -57,15 +56,15 @@ private static class CountingPostInsertTransactionBoundaryListener implements Po @Override public void onPostInsert(PostInsertEvent event) { - event.getSession().getActionQueue().registerProcess( + event.getSession().getTransactionCompletionCallbacks().registerCallback( new BeforeTransactionCompletionProcess() { @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { beforeCounter.increase(); } } ); - event.getSession().getActionQueue().registerProcess( + event.getSession().getTransactionCompletionCallbacks().registerCallback( new AfterTransactionCompletionProcess() { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) { diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/jta/JtaTransactionAfterCallbackTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/jta/JtaTransactionAfterCallbackTest.java index 4ccb87a96620..b0912c29b214 100644 --- a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/jta/JtaTransactionAfterCallbackTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/jta/JtaTransactionAfterCallbackTest.java @@ -17,6 +17,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.internal.synchronization.AuditProcess; import org.hibernate.envers.internal.synchronization.AuditProcessManager; @@ -80,7 +81,7 @@ public void testAuditProcessManagerFlushedOnTransactionTimeout() throws Exceptio // Register before completion callback // The before causes this thread to wait until the Reaper thread aborts our transaction final SessionImplementor session = entityManager.unwrap( SessionImplementor.class ); - session.getActionQueue().registerProcess( new BeforeCallbackCompletionHandler() ); + session.getTransactionCompletionCallbacks().registerCallback( new BeforeCallbackCompletionHandler() ); TestingJtaPlatformImpl.transactionManager().commit(); } @@ -106,7 +107,7 @@ public void testAuditProcessManagerFlushedOnTransactionTimeout() throws Exceptio public static class BeforeCallbackCompletionHandler implements BeforeTransactionCompletionProcess { @Override - public void doBeforeTransactionCompletion(SessionImplementor session) { + public void doBeforeTransactionCompletion(SharedSessionContractImplementor session) { try { // Wait for the transaction to be rolled back by the Reaper thread. final Transaction transaction = TestingJtaPlatformImpl.transactionManager().getTransaction();