Hibernate - CMT EJB using a software transactional idiom

What happens when the following software transactions and session idioms are used with CMT (EJB3) and Hibernate Core to use CMT?
It was assumed that the current CMT transaction is required and will start with the default use of @TransactionAttribute(REQUIRED)

  • Will the hibernate transaction connect to the current CMT on beginTransaction() ?
  • Will commit() try to complete a sleep transaction immediately or wait for the current CMT to execute?
  • What happens when a session is closed in CMT?

Q. The behavior depends on whether the current session is attached to the 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, I currently use one database, and it works fine using JDBC programmatic transactions with Hibernate. Now the application also uses JMS-Queue for mail messages and wants to combine it into a global CMT transaction.

Edit:

At the moment, I do not use EntityManager in the application at all, and also would like to keep the code portable in non-managed environments.

Hibernate.cfg.xml hibernation configuration 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 Search

SessionFactory is created inside a singleton EJB. Seized unnecessary things.

 @Startup @Singleton public class SessionManager { private SessionFactory sessionFactory; public SessionManager() { final Configuration configuration = new Configuration().configure(); this.sessionFactory = configuration.buildSessionFactory(); } } 
+6
source share
4 answers

As Bow noted, this is not a way to encode it in a CMT environment. In any case, the session.beginTransaction() 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, start the transaction. Otherwise, continue the new work in the context of the existing underlying transaction.

tx.rollback() also safe. This is not indicated in the document, but CMTTransaction actually does getTransaction().setRollbackOnly() , i.e. Just marks TX for rollback. The end does not actually transmit the TX, but can clear the session. Real commit will violate the semantics of the transaction if more than one resource is involved.

+1
source

With CMT (container-driven transaction), you don't declare anything like tx = session.beginTransaction (); you let the container do the work for you. You specify only when and if the container supports transactions. Check oracle document Java EE Tutorial 5

Suppose you have an EJB, its transaction is required by default. This way hibernate will actually be tied to this area of ​​the transaction.

in the following example with the first ejb without a transaction that invokes another with 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 doSomething () method is executed, the container will be emptied and commit the update for the database, since external ejb does not have a transaction in progress. This only works if the data source is also provided by the container.

+1
source

A session (in the JPA persistence context , which is bound to an EntityManager instance), is a snapshot of the "in memory" state of a subset of the database schema. The volume of your session will vary depending on your configuration. In a standard web application, you will have one session for each request.

You can have many instances of a session with a different state at the same time, the session is isolated from each other (the operation performed in the session does not appear in another)

A transaction is a unit of work (theoretically, a session too). It is tied to the RDBMS core transaction system and tied to the session in which it was opened.

In the context of the “Managed Container Manager” (that you are invoking CMT), the container will associate your session with a specific area and propagate transactions according to the @Transactional annotation @Transactional when invoking methods.

Actually what happens: your container maintains a session instance somewhere and can provide it to your ejb instance using the @PersistenceContext annotation. You manually create a new instance of the session using sessionFactory.openSession() , open a transaction, and perform your operations. A managed session instance cannot see any of these changes until you complete the transaction, manually reset or close your own session and manually activate the update on the container.

The getCurrentSession() method is a hibernation mechanism that acts as a mechanism for managing the container session area in the context of Java SE (without the container). I suppose (but I have no idea about implementing a sleeping JPA) that it will not return a container managed session, but I could be wrong about this. ( change )

The right solution here would be to retrieve the current instance of the container-managed session using @PersistenceContext and control the propagation of transactions using the @Transactional annotation.

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

See Luk's comment below

FYI See Container Managed Transactions

EDIT (according to issue questions)

See The Difference Between "jta-datasource" and "Resource-Local" DataSource?

It actually seems like I was wrong and that you don't need to use persistence context wrap from the container, but you MUST use JTA transactions.

From the EJB 3.0 Specification, Section 13.3.4 Beans Enterprise Using Managed Container Transaction Demarcation:

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

That means you can use

 sessionFactory.getCurrentSession() 

but you should not use tx = session.beginTransaction (), but instead

 @TransactionAttribute(TransactionAttributeType.REQUIRED) 

See transaction demarcation with the EJB / CMT section in the jboss document above

0
source

I learned something new from your question since I did not know that Hibernate can be configured in this way (although it was clear that it supports JTA). In any case, according to the documentation, it seems that you should not configure it to use JTA, as described here :

If your persistence level is running on the application server (for example, behind an EJB beans session), every data source connection received by Hibernate will automatically be part of the global JTA transaction. You can also install a standalone JTA implementation and use it without an EJB. Hibernate offers two JTA integration strategies.

Also see examples in the documentation, as you do not need to open transactions in the context of CMT. However, if you want to control transaction demarcation, check out these BMT examples.

0
source

All Articles