From d9ed7bc9a9b362cab20dc9c67559513efe9b81e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 12 Jan 2021 19:08:54 +0100 Subject: [PATCH] Allow setting a different connection handling mode and auto-close mode for non-injected sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this patch, hopefully people can safely use EntityManagerFactory.createEntityManager() and opening transactions manually? Signed-off-by: Yoann Rodière --- .../runtime/boot/FastBootMetadataBuilder.java | 3 -- .../orm/runtime/session/JTASessionOpener.java | 50 +++++++++++++++++++ .../session/TransactionScopedSession.java | 4 +- 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/JTASessionOpener.java diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java index ab7a594f89bd2..44ea98d385395 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java @@ -255,9 +255,6 @@ private MergedSettings mergeSettings(PersistenceUnitDescriptor persistenceUnit) cfg.putIfAbsent(AvailableSettings.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION); - // Auto-close sessions before transaction completion, as they should be when using JTA. - cfg.putIfAbsent(AvailableSettings.AUTO_CLOSE_SESSION, "true"); - if (readBooleanConfigurationValue(cfg, WRAP_RESULT_SETS)) { LOG.warn("Wrapping result sets is not supported. Setting " + WRAP_RESULT_SETS + " to false."); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/JTASessionOpener.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/JTASessionOpener.java new file mode 100644 index 0000000000000..a9e22a266849e --- /dev/null +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/JTASessionOpener.java @@ -0,0 +1,50 @@ +package io.quarkus.hibernate.orm.runtime.session; + +import org.hibernate.FlushMode; +import org.hibernate.Session; +import org.hibernate.SessionBuilder; +import org.hibernate.SessionFactory; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; + +/** + * A delegate for opening a JTA-enabled Hibernate ORM session. + *

+ * The main purpose of this class is to cache session options when possible; + * if we didn't care about caching, we could just replace any call to + */ +public class JTASessionOpener { + public static JTASessionOpener create(SessionFactory sessionFactory) { + final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = sessionFactory + .unwrap(SessionFactoryImplementor.class).getCurrentTenantIdentifierResolver(); + if (currentTenantIdentifierResolver == null) { + // No tenant ID resolver: we can cache the options. + return new JTASessionOpener(sessionFactory, createOptions(sessionFactory)); + } else { + // There is a tenant ID resolver: we cannot cache the options. + return new JTASessionOpener(sessionFactory, null); + } + } + + private static SessionBuilder createOptions(SessionFactory sessionFactory) { + return sessionFactory.withOptions() + .autoClose(true) // .owner() is deprecated as well, so it looks like we need to rely on deprecated code... + .connectionHandlingMode( + PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION) + .flushMode(FlushMode.ALWAYS); + } + + private final SessionFactory sessionFactory; + private final SessionBuilder cachedOptions; + + public JTASessionOpener(SessionFactory sessionFactory, SessionBuilder cachedOptions) { + this.sessionFactory = sessionFactory; + this.cachedOptions = cachedOptions; + } + + public Session openSession() { + SessionBuilder options = cachedOptions != null ? cachedOptions : createOptions(sessionFactory); + return options.openSession(); + } +} diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java index f4046bfaee123..87661331c38c9 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedSession.java @@ -59,6 +59,7 @@ public class TransactionScopedSession implements Session { private final TransactionManager transactionManager; private final TransactionSynchronizationRegistry transactionSynchronizationRegistry; private final SessionFactory sessionFactory; + private final JTASessionOpener jtaSessionOpener; private final String unitName; private final String sessionKey; private final Instance requestScopedSessions; @@ -71,6 +72,7 @@ public TransactionScopedSession(TransactionManager transactionManager, this.transactionManager = transactionManager; this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; this.sessionFactory = sessionFactory; + this.jtaSessionOpener = JTASessionOpener.create(sessionFactory); this.unitName = unitName; this.sessionKey = this.getClass().getSimpleName() + "-" + unitName; this.requestScopedSessions = requestScopedSessions; @@ -82,7 +84,7 @@ SessionResult acquireSession() { if (session != null) { return new SessionResult(session, false, true); } - Session newSession = sessionFactory.openSession(); + Session newSession = jtaSessionOpener.openSession(); // The session has automatically joined the JTA transaction when it was constructed. transactionSynchronizationRegistry.putResource(sessionKey, newSession); // No need to flush or close the session upon transaction completion: