11
votes

I'm trying to use a StatelessSession to do some bulk inserts in an OSGi environment (Karaf 4.0.7), but when I try to commit my transaction I get

be.ikan.lib.orm.base.exceptions.PersistenceBrokerException: org.hibernate.TransactionException: Cannot retrieve the TransactionManager OSGi service!
at be.ikan.lib.orm.hibernate.broker.HibernateStatelessPersistenceBrokerImpl.commitTransaction(HibernateStatelessPersistenceBrokerImpl.java:118)[79:be.ikan.lib.orm:7.0.0]
at be.ikan.scm4all.business.server.bs.pack.PackageServiceImpl.createLevelRequestFileRevisionAssociations(PackageServiceImpl.java:1412)[72:be.ikan.scm4all.daemons.server:5.8.0]
at be.ikan.scm4all.phases.core.level.LinkFileRevisionsPhase.execute(LinkFileRevisionsPhase.java:99)[72:be.ikan.scm4all.daemons.server:5.8.0]
at Proxy5a8c2944_a0d5_4e21_a1b7_3f30296f5993.execute(Unknown Source)[:]
at be.ikan.scm4all.phases.impl.DefaultPhaseExecutionImpl.execute(DefaultPhaseExecutionImpl.java:152)[114:be.ikan.scm4all.daemons.shared:5.8.0]
at be.ikan.scm4all.daemons.server.monitor.MonitorThread.run(MonitorThread.java:231)[72:be.ikan.scm4all.daemons.server:5.8.0]
Caused by: org.hibernate.TransactionException: Cannot retrieve the TransactionManager OSGi service!
at org.hibernate.osgi.OsgiJtaPlatform.retrieveTransactionManager(OsgiJtaPlatform.java:51)[62:org.hibernate.osgi:5.2.17.Final]
at org.hibernate.osgi.OsgiJtaPlatform.getCurrentStatus(OsgiJtaPlatform.java:98)[62:org.hibernate.osgi:5.2.17.Final]
at org.hibernate.internal.StatelessSessionImpl.flushBeforeTransactionCompletion(StatelessSessionImpl.java:667)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.internal.StatelessSessionImpl.beforeTransactionCompletion(StatelessSessionImpl.java:644)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:156)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)[60:org.hibernate.core:5.2.17.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)[60:org.hibernate.core:5.2.17.Final]
at be.ikan.lib.orm.hibernate.broker.HibernateStatelessPersistenceBrokerImpl.commitTransaction(HibernateStatelessPersistenceBrokerImpl.java:114)[79:be.ikan.lib.orm:7.0.0]
... 5 more
Caused by: org.hibernate.TransactionException: Cannot retrieve the TransactionManager OSGi service!
at org.hibernate.osgi.OsgiJtaPlatform.retrieveTransactionManager(OsgiJtaPlatform.java:46)[62:org.hibernate.osgi:5.2.17.Final]

I don't use JTA for managing my transactions, I set hibernate.transaction.coordinator_class=jdbc. The code that uses a regular Sesssion runs fine. There's another portion of the application that runs in a non-OSGi environment, and there the StatelessSession works fine.

I tracked it down into the Hibernate source and found in org.hibernate.internal.StatelessSessionImpl :

@Override
public void flushBeforeTransactionCompletion() {
    boolean flush = false;
    try {
        flush = (
                !isClosed()
                        && !isFlushModeNever()
                        && !JtaStatusHelper.isRollback(
                        getJtaPlatform().getCurrentStatus()
                ) );
    }
    catch (SystemException se) {
        throw new HibernateException( "could not determine transaction status in beforeCompletion()", se );
    }
    if ( flush ) {
        managedFlush();
    }
}

Since the session isn't closed, and StatelessSessionImpl.isFlushModeNever() always returns false, the method getJtaPlatform() always gets called, which ultimately fails because it can find a JtaPlatform (org.hibernate.osgi.OsgiJtaPlatform), but it isn't configured (because I don't use it).

So does that mean you can't use a StatelessSession without configuring JTA ?

I'm using Hibernate 5.2.17. This approach worked fine in Hibernate 4.3.7 by the way, but then there was no hibernate-osgi bundle, and it seems the StatelessSessionImpl class has undergone some significant changes.


After some further investigation, I managed to get it working by installing the Karaf feature "transaction". In the karaf console, I executed feature:install transaction. This installs an OSGi Transaction Manager (provided by Apache Aries), which registers a Service implementation for javax.transaction.TransactionManager, which allows the org.hibernate.osgi.OsgiJtaPlatform class to locate it and gets rid of the above Exception. The code seems to work after that : transactions are committed without problems and the data is persisted.

But I'm still left with the question : does the StatelessSession use JTA Transactions or not ?

In the non-OSGi portion of the application, which runs in Tomcat, I found in the debug logs

2018-08-29 13:29:00,615 [localhost-startStop-1] DEBUG JtaPlatformInitiator - No JtaPlatform was specified, checking resolver
2018-08-29 13:29:00,617 [localhost-startStop-1] DEBUG JtaPlatformResolverInitiator - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver]
2018-08-29 13:29:00,629 [localhost-startStop-1] DEBUG StandardJtaPlatformResolver - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]

So there Hibernate uses org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform.

In the karaf log, I found the properties with which the SessionFactory is instantiated :

2018-08-29 14:45:48,897 | DEBUG | e: pid=[server]) | SessionFactoryImpl               | 72 - org.jboss.logging.jboss-logging - 3.3.1.Final | Building session factory
2018-08-29 14:45:48,936 | DEBUG | e: pid=[server]) | SessionFactoryImpl               | 72 - org.jboss.logging.jboss-logging - 3.3.1.Final | Session factory constructed with filter configurations : {}
2018-08-29 14:45:48,937 | DEBUG | e: pid=[server]) | SessionFactoryImpl               | 72 - org.jboss.logging.jboss-logging - 3.3.1.Final | Instantiating session factory with properties: {<a lot of properties>, hibernate.transaction.jta.platform=org.hibernate.osgi.OsgiJtaPlatform@7d6ea302, <more properties>}
2018-08-29 14:45:48,969 | DEBUG | e: pid=[server]) | SessionFactoryImpl               | 72 - org.jboss.logging.jboss-logging - 3.3.1.Final | Instantiated session factory

I tried setting the jta platform to NoJtaPlatform by adding to hibernate.properties

hibernate.transaction.jta.platform=org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform

But this had no effect : it still logged that the session factory was created using OsgiJtaPlatform.

If I can somehow configure Hibernate so that it uses the NoJtaPlatform in Karaf, then I think I wouldn't need the extra "transaction" feature. It would also convince me that the application only uses JDBC transactions, and not JTA.

1

1 Answers

2
votes

The way I see the things. In order to get Non JTA transactions you need to convince Hibernate that it is not in JTA enabled environment. Which means don't enable JDBC don't enable JMS don't enable tansactions

Here is what is happening>

  1. Hibernate view. From hibernate point of view it is the “org.hibernate.engine.transaction.spi.TransactionFactory” that decides what kind of environment it is used within and depending on the environmet it will initialize JDBC based or JTA based transactions manager. This yu can read in chapter 2.2 and the subsequent references https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch02.html

  2. Karaf according to the documentation point 4.16.4 https://karaf.apache.org/manual/latest/#_transaction_jta

Apache Karaf provides container managed transactions, available as OSGi services.

And the next sentence is that

However, the transaction feature is installed (as a transitive dependency) when installing enterprise features (like jdbc or jms features for instance).

This basicly means that you willget a JTA transactio manager once you have enablethe JDBC service.

On another note since you are trying to use StatelessHibernateSession my presumption is that you are trying to achieve performance. A friendly advice :)

  1. Make sure you fetch several collections at once while reading thought @BatchSize
  2. Make sure you have good allocation size for the sequences when you generate Ids
  3. Ensure you have configured jdbc batch size to good value.
  4. Make sure you have lazy relationships with Fetch.Select

And forget about the Stateless session :) You might be surprised how much performance you can squeeze out of hibernate alone.