Description:
|
In Hibernate 3.6.10, when creating JtaTransaction from JtaTransactionFactory UserTransaction was always setted:
{code}
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext) throws HibernateException { UserTransaction ut = getUserTransaction(); return new JTATransaction( ut, jdbcContext, transactionContext ); }
{code}
This behavior changed in Hibernate 4.0.1 as UserTransaction will only get setted if explicitly beginning a new transaction ; UserTransaction is no longer passed by the JtaTransactionFactory:
{code}
protected void doBegin() { LOG.debug( "begin" ); userTransaction = jtaPlatform().retrieveUserTransaction(); if ( userTransaction == null ) { throw new TransactionException( "Unable to locate JTA UserTransaction" ); } ... }
{code}
This change of affect the JtaTransactionFactory::isJoinableJtaTransaction method behavior:
{code}
public boolean isJoinableJtaTransaction(TransactionCoordinator transactionCoordinator, JtaTransaction transaction) { try { // Essentially: // 1) If we have a local (Hibernate) transaction in progress // and it already has the UserTransaction cached, use that // UserTransaction to determine the status. // 2) If a transaction manager has been located, use // that transaction manager to determine the status. // 3) Finally, as the last resort, try to lookup the // UserTransaction via JNDI and use that to determine the // status. if ( transaction != null ) { UserTransaction ut = transaction.getUserTransaction(); if ( ut != null ) { return JtaStatusHelper.isActive( ut ); } } ...
{code}
Hibernate 4.0.1: transaction.getUserTransaction() == null Hibernate 3.6.10: transaction.getUserTransaction() != null
as stated in javadoc, if user transaction as not been set in 1), it will fallback on jtaPlatform.retrieveTransactionManager 2) and will later fail with "proxy handle is no longer valid" as UserTransaction as not been set. see forum reference : https://forum.hibernate.org/viewtopic.php?f=1&t=1014848
As a workaround I've hacked the JtaTransaction::getUserTransaction() to:
{code}
private static Field localStatus; @SuppressWarnings( {"UnusedDeclaration"}) public UserTransaction getUserTransaction() { if (userTransaction != null){ return userTransaction; } userTransaction = jtaPlatform().retrieveUserTransaction(); if ( userTransaction == null ) { throw new TransactionException( "Unable to locate JTA UserTransaction" ); } if (localStatus == null){ try{ localStatus = AbstractTransactionImpl.class.getDeclaredField("localStatus"); localStatus.setAccessible(true); } catch (Exception e){ throw new RuntimeException(e); } } try{ localStatus.set(this, LocalStatus.ACTIVE); } catch (Exception e){ throw new RuntimeException(e); } return userTransaction; }
{code}
which seem to resolve the issue.
Here's how Configuration and SessionFactory are created :
{code}
configuration = new Configuration(); configuration.setProperty("hibernate.connection.datasource", datasourceName); // Hibernate Configuration Properties configuration.setProperty("hibernate.dialect", "com.XXX.MySQL5InnoDBDialect"); configuration.setProperty("hibernate.show_sql", "false"); configuration.setProperty("hibernate.format_sql", "false"); configuration.setProperty("hibernate.max_fetch_depth", "0"); // recommended values between 0 and 3 configuration.setProperty("hibernate.default_batch_fetch_size", "16"); // recommended values 4, 8, 16 configuration.setProperty("hibernate.order_updates", "true"); configuration.setProperty("hibernate.generate_statistics", "true"); configuration.setProperty("hibernate.use_sql_comments", "false"); // Hibernate JDBC and Connection Properties configuration.setProperty("hibernate.jdbc.batch_size", "30"); // recommended values between 5 and 30 configuration.setProperty("hibernate.jdbc.batch_versioned_data", "true"); configuration.setProperty("hibernate.connection.provider_class", "com.XXX.DatasourceConnectionProvider"); // Hibernate Cache Properties configuration.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.infinispan.InfinispanRegionFactory"); configuration.setProperty("hibernate.cache.use_minimal_puts", "true"); configuration.setProperty("hibernate.cache.use_query_cache", "false"); // true to activate L2 cache configuration.setProperty("hibernate.cache.use_second_level_cache", "false"); // true to activate L2 cache configuration.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory"); configuration.setProperty("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup"); configuration.setProperty("jta.UserTransaction", JBossAppServerJtaPlatform.UT_NAME); configuration.setProperty("hibernate.transaction.jta.platform", "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"); // Miscellaneous Properties configuration.setProperty("hibernate.current_session_context_class", "jta"); // deactivate hibernate envers configuration.setProperty("hibernate.listeners.envers.autoRegister", "false");
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder().withApplicationClassLoader(this.getClass().getClassLoader()).build(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder(bootstrapServiceRegistry).applySettings(configuration.getProperties()).buildServiceRegistry(); SessionFactory sessionFactory = (SessionFactory) configuration.buildSessionFactory(serviceRegistry);
{code}
Is there any reason why UserTransaction is not injected anymore at creation time ? Is my [ugly] workaround valid ?
Thanks,
|