6

What happens when the following programmatic transaction and session idiom is used with within CMT (EJB3) and Hibernate Core is set up to use CMT?
Assumed a current CMT transaction is required and started using default @TransactionAttribute(REQUIRED)

  1. Will the hibernate transaction join the current CMT on beginTransaction()?
  2. Will commit() try to commit the hibernate transaction immediately or wait until the current CMT commits?
  3. What happens when closing the session in CMT?

B. Does the behavior depends if the current-session is bound to CMT using getCurrentSession()?

// A: openSession()
// B: getCurrentSession();
Session session = sessionFactory.openSession(); 
Transaction tx = null;
try
{
    tx = session.beginTransaction();

    // do some work

    tx.commit();
}
catch (final RuntimeException e)
{
    try 
    {
        tx.rollback();
    }
    catch (final RuntimeException e)
    {
        // log error
    }
    throw e;
}
finally
{
    session.close();
}

In my application currently i am using a single database and it worked fine using programmatic JDBC transactions with Hibernate. Now the application also uses a JMS-Queue for Mail messaging and would like to merge it into the global CMT transaction.

Edit:

At the moment i am not using EntityManager in the application at all and also would like to keep code portable to non-managed environments.

Hibernate configuration hibernate.cfg.xml to enable CMT:

Hibernate 4.2.6 and Glassfish 3.1.2

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.autocommit">false</property>
<property name="hibernate.connection.datasource">jdbc/datasource</property>
<property name="hibernate.current_session_context_class">jta</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</property>
<property name="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.SunOneJtaPlatform</property>

SessionFactory retrieval

SessionFactory is build within an singleton EJB. Stripped unnecessary stuff.

@Startup
@Singleton
public class SessionManager
{
    private SessionFactory sessionFactory;

    public SessionManager()
    {
        final Configuration configuration = new Configuration().configure();
        this.sessionFactory = configuration.buildSessionFactory();
    }
}
djmj
  • 5,579
  • 5
  • 54
  • 92

4 Answers4

1

With CMT (Container Managed Transaction) you don't declare anything like tx = session.beginTransaction(); you let the container do the work for you. You would only specify when and if the container supports transactions. Check out the oracle doc Java EE 5 Tutorial

Lets say you have an EJB, its default transaction scope is Required. So hibernate will actually be bound to that transaction scope.

following example with first ejb with no transaction which calls another one with a cmt:

@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements TransactionInterface{

   @EJB BusinessBean businessBean;

   public method1(){
       businessBean.doSomething();
   }
}

@TransactionAttribute(REQUIRED)
@Stateful
public class BusinessBean implements BusinessInterface{

    @PersistenceContext(unitName = "some-persistence-unit")
    private EntityManager entityManager;

    public void doSomething(){
        Someclass entity = entityManager.finde(Someclass.class, 1) // Find entity with id 1
        entity.setData("somedata");
    }
}

when the methode doSomething() is done, the container will flush and commit the update to the databese since the outer ejb doesn't have a running transaction. This only works if the datasource is also provided by the container

Luk
  • 431
  • 7
  • 14
  • `you don't declare anything like tx = session.beginTransaction()` That's why i was asking what happens if you mix CMT managed hibernate transaction with programmatic transaction handling to keep code portable. – djmj Jan 21 '14 at 23:18
1

As Luk has pointed out, this is not the way to code it in CMT enviroment. Anyway, the session.beginTransaction() part is safe here according to http://docs.jboss.org/hibernate/annotations/3.5/api/org/hibernate/Session.html#beginTransaction%28%29 which says

If a new underlying transaction is required, begin the transaction. Otherwise continue the new work in the context of the existing underlying transaction

tx.rollback() is also safe. It's not stated in the doc, but a CMTTransaction actually executes getTransaction().setRollbackOnly(), i.e. it simply marks the TX for rollback. The commit actually does not commit the TX, but may flush the session. A real commit would violate the transaction semantics if more than one resource is involved.

Drunix
  • 3,313
  • 8
  • 28
  • 50
  • Thanks for the answer. For me the description of `session.beginTransaction()` was not clear enough which transaction is meant (Hibernate or JTA). – djmj Jan 24 '14 at 12:52
  • http://docs.jboss.org/hibernate/annotations/3.5/api/org/hibernate/Transaction.html Seems it can be both and also pure jdbc one, just depend on the TransactionFactory – Gab Jan 24 '14 at 22:28
  • In general yes, but not in the setting of the question (unless you have misconfigured your TransactionFactory for this case). – Drunix Jan 25 '14 at 08:46
0

A session (in JPA persistence context, which is tied to an EntityManager instance) is an "in-memory" snapshot of the state of a database schema subset. Depending of your configuration the scope of your session will vary. In a standard web application you will have one session per request.

You can have many instances of session with different state at same time, session are isolated each others (operation performed on a session are not visible in another one)

A transaction is a unit of work (in theory a session too). It's bound to underlying RDBMS transaction system and tied to the session it was open on.

In a "container managed entity manager" context (what you're calling CMT), the container will be in charge of binding your session to the defined scope and propagate the transactions according to encountered @Transactional annotation along methods invocation.

Actually what is happening : your container maintains an instance of session somewhere and would be able to provide it to your ejb instance using a @PersistenceContext annotation. You are manually building a new session instance using a sessionFactory.openSession(), opening a transaction on it and performing your operations. The managed session instance can't see any of those modification until you commit the transaction, manually flushed or closed your custom session and manually trigger a refresh on the container one.

The getCurrentSession() method is an hibernate specific mechanism which act as the container session scope management mechanism in a Java SE context (no container). I suppose (but I've no idea of the hibernate JPA implementation) that it won't return the container managed session, but I may be wrong on this point. (edit I am)

Here a proper solution will be to retrieve the current container managed session instance using @PersistenceContext and to manage transaction propagation using @Transactional annotation.

See https://community.jboss.org/wiki/SessionsAndTransactions

See Luk anwser below

FYI See Container-Managed Transactions

EDIT (according to question edition)

See Difference between a "jta-datasource" and a " resource-local " datasource?

In fact it seems I was effectively wrong and that you are not required to use persistence context injection from container but you MUST use JTA Transactions.

From the EJB 3.0 spec, section 13.3.4 Enterprise Beans Using Container-Managed Transaction Demarcation:

The enterprise bean’s business methods [...] must not attempt to obtain or use the javax.transaction.UserTransaction interface.

It means you can use

sessionFactory.getCurrentSession() 

but you mustn't use tx = session.beginTransaction() but instead

@TransactionAttribute(TransactionAttributeType.REQUIRED) 

See Transaction demarcation with EJB/CMT section in the jboss doc above

Community
  • 1
  • 1
Gab
  • 7,869
  • 4
  • 37
  • 68
  • From your linked jboss doc I do not see that I must not use it. Or i missed it. It only points out a suggestion (same as official hibernate docs) `Instead of coding the begin, commit, and rollback of your transactions into your application you could use a declarative approach.` – djmj Jan 21 '14 at 23:22
  • indeed there is nothing in jboss doc that clearly forbid this approach, however the advised way is to use declarative one. If the effective implementation type returned by `beginTransaction()`is `CMTTransaction` your approach might work. – Gab Jan 22 '14 at 10:39
  • I'am probably too much tied to JPA way, I didn't know that hibernate was so flexible, your question is an excellent one and clearly deserve more interest – Gab Jan 22 '14 at 10:41
0

I learned something new from your question, as I didn't know Hibernate can be configured this way (although it was clear that it supports JTA). Anyway according to the documentation it seems that you are not forced to configure it in order to use JTA, as it is described here :

If your persistence layer runs in an application server (for example, behind EJB session beans), every datasource connection obtained by Hibernate will automatically be part of the global JTA transaction. You can also install a standalone JTA implementation and use it without EJB. Hibernate offers two strategies for JTA integration.

Also see the examples in the documentation, as you do not need to open any transactions in a CMT context. However, if you want to control the transaction demarcations, check those BMT examples.

V G
  • 18,822
  • 6
  • 51
  • 89
  • 1
    Thanks for your answer. I now tested and debugged it: The hibernate transaction retrieved by `beginTransaction()` is of type `CMTTransaction` with status set to `JOINED`. And it commits on method return, when container transaction will also commit. I added a breakpoint right before method return to check it. Using container-jta bound session via `getCurrentSession()` same behavior is observed (omitting `session.close()`, since container closes it). Now i am confused. – djmj Jan 18 '14 at 18:17
  • Hibernate `CMTTransaction` has `join()` method to join it with underlying transaction, but in my setup it is joined by default. – djmj Jan 18 '14 at 18:18
  • Would you mind showing us how you get the sesionFactory and your persistence.xml/hibernate config? – V G Jan 18 '14 at 18:25
  • I changed my answer with a link to the explanation. Sorry for the wrong initial answer, but at least you now know how Hibernate officially works (and that this feature is actually documented). – V G Jan 19 '14 at 20:29