From 41616b643c19a92c90e9bd82cdbeab68b6d4d2da Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 17 Dec 2025 19:51:35 +0100 Subject: [PATCH 1/3] add an experimental JakartaStandardJtaPlatform --- .../internal/JakartaStandardJtaPlatform.java | 115 ++++++++++++++++++ .../JtaPlatformResolverInitiator.java | 6 +- .../internal/StandardJtaPlatformResolver.java | 23 +++- .../internal/WebSphereLibertyJtaPlatform.java | 3 +- ...SingleTableAbstractDiscriminatorTests.java | 2 +- 5 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java new file mode 100644 index 000000000000..0f28bd30e3ed --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.transaction.jta.platform.internal; + +import jakarta.transaction.HeuristicMixedException; +import jakarta.transaction.HeuristicRollbackException; +import jakarta.transaction.NotSupportedException; +import jakarta.transaction.RollbackException; +import jakarta.transaction.SystemException; +import jakarta.transaction.Transaction; +import jakarta.transaction.TransactionManager; +import jakarta.transaction.TransactionSynchronizationRegistry; +import jakarta.transaction.UserTransaction; +import org.hibernate.Incubating; + +import javax.transaction.xa.XAResource; + +/** + * A {@link org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform} + * that in principle is supposed to work in any Jakarta EE container. This + * implementation is crippled by the fact that it is unable to suspend and + * resume transactions. + * @since 4.0 + * @author Gavin King + */ +@Incubating +public class JakartaStandardJtaPlatform extends AbstractJtaPlatform + implements TransactionManager, Transaction { + + public static final JakartaStandardJtaPlatform INSTANCE = new JakartaStandardJtaPlatform(); + public static final String USER_TRANSACTION = "java:comp/UserTransaction"; + public static final String TRANSACTION_SYNCHRONIZATION_REGISTRY = + "java:comp/TransactionSynchronizationRegistry"; + + @Override + protected TransactionManager locateTransactionManager() { + return this; + } + + @Override + protected UserTransaction locateUserTransaction() { + return (UserTransaction) jndiService().locate( USER_TRANSACTION ); + } + + @Override + protected JtaSynchronizationStrategy getSynchronizationStrategy() { + return new SynchronizationRegistryBasedSynchronizationStrategy( + this::getTransactionSynchronizationRegistry ); + } + + private TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { + return (TransactionSynchronizationRegistry) + jndiService().locate( TRANSACTION_SYNCHRONIZATION_REGISTRY ); + } + + @Override + public void begin() throws NotSupportedException, SystemException { + locateUserTransaction().begin(); + } + + @Override + public void commit() + throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException { + locateUserTransaction().commit(); + } + + @Override + public void rollback() throws IllegalStateException, SecurityException, SystemException { + locateUserTransaction().rollback(); + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + locateUserTransaction().setRollbackOnly(); + } + + @Override + public int getStatus() throws SystemException { + return locateUserTransaction().getStatus(); + } + + @Override + public Transaction getTransaction() throws SystemException { + return this; + } + + @Override + public void setTransactionTimeout(int seconds) throws SystemException { + locateUserTransaction().setTransactionTimeout( seconds ); + } + + @Override + public boolean delistResource(XAResource xaRes, int flag) { + throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + } + + @Override + public boolean enlistResource(XAResource xaRes) { + throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + } + + @Override + public Transaction suspend() throws SystemException { + throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + } + + @Override + public void resume(Transaction tobj) { + throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformResolverInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformResolverInitiator.java index 0b27512fe3fd..f161006a50d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformResolverInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JtaPlatformResolverInitiator.java @@ -8,7 +8,7 @@ import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.boot.registry.selector.spi.StrategySelector; -import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.TransactionSettings; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatformResolver; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -24,8 +24,8 @@ public class JtaPlatformResolverInitiator implements StandardServiceInitiator configurationValues, ServiceRegistryImplementor registry) { - final Object setting = configurationValues.get( AvailableSettings.JTA_PLATFORM_RESOLVER ); - final JtaPlatformResolver resolver = + final Object setting = configurationValues.get( TransactionSettings.JTA_PLATFORM_RESOLVER ); + final var resolver = registry.requireService( StrategySelector.class ) .resolveStrategy( JtaPlatformResolver.class, setting ); if ( resolver == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/StandardJtaPlatformResolver.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/StandardJtaPlatformResolver.java index 7bc0796eec6c..db02cd738c3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/StandardJtaPlatformResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/StandardJtaPlatformResolver.java @@ -15,6 +15,11 @@ import org.jboss.logging.Logger; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import static org.hibernate.engine.transaction.jta.platform.internal.JakartaStandardJtaPlatform.TRANSACTION_SYNCHRONIZATION_REGISTRY; + /** * @author Steve Ebersole */ @@ -25,11 +30,11 @@ public class StandardJtaPlatformResolver implements JtaPlatformResolver { @Override public JtaPlatform resolveJtaPlatform(Map configurationValues, ServiceRegistryImplementor registry) { - final ClassLoaderService classLoaderService = registry.requireService( ClassLoaderService.class ); + final var classLoaderService = registry.requireService( ClassLoaderService.class ); // Initially look for a JtaPlatformProvider - for ( JtaPlatformProvider provider : classLoaderService.loadJavaServices( JtaPlatformProvider.class ) ) { - final JtaPlatform providedPlatform = provider.getProvidedJtaPlatform(); + for ( var provider : classLoaderService.loadJavaServices( JtaPlatformProvider.class ) ) { + final var providedPlatform = provider.getProvidedJtaPlatform(); LOG.tracef( "Located JtaPlatformProvider [%s] provided JtaPlatform : %s", provider, providedPlatform ); if ( providedPlatform!= null ) { return providedPlatform; @@ -87,8 +92,18 @@ public JtaPlatform resolveJtaPlatform(Map configurationValues, ServiceRegis catch (ClassLoadingException ignore) { } + // Fallback for EE environment + try { + new InitialContext().lookup( TRANSACTION_SYNCHRONIZATION_REGISTRY ); + LOG.tracef( "Could not determine JtaPlatform, using default [%s]", JakartaStandardJtaPlatform.class.getName() ); + return JakartaStandardJtaPlatform.INSTANCE; + } + catch (NamingException ne) { + //ignore + } + // Finally, return the default... - LOG.tracef( "Could not resolve JtaPlatform, using default [%s]", NoJtaPlatform.class.getName() ); + LOG.tracef( "Could not determine JtaPlatform, using default [%s]", NoJtaPlatform.class.getName() ); return NoJtaPlatform.INSTANCE; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/WebSphereLibertyJtaPlatform.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/WebSphereLibertyJtaPlatform.java index eacd3c6851f8..bd98cbb6e865 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/WebSphereLibertyJtaPlatform.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/WebSphereLibertyJtaPlatform.java @@ -63,7 +63,8 @@ public int getCurrentStatus() throws SystemException { @Override public void registerSynchronization(Synchronization synchronization) { try { - NullnessUtil.castNonNull( retrieveTransactionManager() ).getTransaction().registerSynchronization(synchronization); + NullnessUtil.castNonNull( retrieveTransactionManager() ).getTransaction() + .registerSynchronization(synchronization); } catch ( RollbackException | SystemException x ) { throw new RuntimeException(x); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/SingleTableAbstractDiscriminatorTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/SingleTableAbstractDiscriminatorTests.java index 566334a9a9ba..49054f1672f9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/SingleTableAbstractDiscriminatorTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/SingleTableAbstractDiscriminatorTests.java @@ -40,7 +40,7 @@ void testNonCompliantModel(ServiceRegistryScope registryScope) { // // > The DiscriminatorValue annotation can only be specified on a concrete entity class. // - // we do not validate this though + // we do not validate this though var model = new MetadataSources( registryScope.getRegistry() ) .addAnnotatedClasses( Root.class, Trunk.class, Branch.class ) .buildMetadata(); From 4236a7f237048cda9c9bfdbb71c36d72ef2ddef9 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 17 Dec 2025 20:38:31 +0100 Subject: [PATCH 2/3] we probably don't really need suspend()/resume() anymore - table generators should work without it, though performance poor without a large enough block size - suspend/resume no longer used for temp table bulk operations - very doubtful that this is needed for reading JDBC metadata --- .../platform/internal/JakartaStandardJtaPlatform.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java index 0f28bd30e3ed..2112b18eb5a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/jta/platform/internal/JakartaStandardJtaPlatform.java @@ -14,6 +14,7 @@ import jakarta.transaction.TransactionSynchronizationRegistry; import jakarta.transaction.UserTransaction; import org.hibernate.Incubating; +import org.jboss.logging.Logger; import javax.transaction.xa.XAResource; @@ -34,6 +35,8 @@ public class JakartaStandardJtaPlatform extends AbstractJtaPlatform public static final String TRANSACTION_SYNCHRONIZATION_REGISTRY = "java:comp/TransactionSynchronizationRegistry"; + private static final Logger LOG = Logger.getLogger( JakartaStandardJtaPlatform.class ); + @Override protected TransactionManager locateTransactionManager() { return this; @@ -104,12 +107,15 @@ public boolean enlistResource(XAResource xaRes) { @Override public Transaction suspend() throws SystemException { - throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + LOG.debug( "Cannot really suspend (JakartaStandardJtaPlatform does not have access to the TransactionManager)" ); + return this; +// throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); } @Override public void resume(Transaction tobj) { - throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); + LOG.debug( "Cannot really resume (JakartaStandardJtaPlatform does not have access to the TransactionManager)" ); +// throw new UnsupportedOperationException( "JakartaStandardJtaPlatform does not have access to the TransactionManager" ); } } From a018ef47d25a7e50d55683691deae9708f8dca17 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 17 Dec 2025 20:42:43 +0100 Subject: [PATCH 3/3] improve Javadoc of JTA settings --- .../src/main/java/org/hibernate/cfg/TransactionSettings.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java index 27b42a1b1d64..49b47158462d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java @@ -46,6 +46,8 @@ public interface TransactionSettings { *
  • the name of a class that implements {@code JtaPlatform}. *
  • short name of a class (sans package name) that implements {@code JtaPlatform}. * + *

    If not specified, the {@linkplain #JTA_PLATFORM_RESOLVER JTA platform resolver} + * is used to obtain an instance of {@code JtaPlatform}. * * @see #JTA_PLATFORM_RESOLVER * @@ -58,6 +60,8 @@ public interface TransactionSettings { * implementation that should be used to obtain an instance of * {@link org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform}. * + * @settingDefault {@link org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver} + * * @since 4.3 */ String JTA_PLATFORM_RESOLVER = "hibernate.transaction.jta.platform_resolver";