Hibernate – How to use Container Managed Transaction (CMT) with JBoss AS 6, Hibernate 3.6, JPA, JTA and EJB3

ejb-3.0hibernatejbossjpajta

I'm attempting to setup a web app using CMT. I've got it running standalone within Eclipse ok, and now I'm trying to get it working within Jboss AS 6, using Struts 1.0. I've chosen CMT because the doco I've read hints that it's the best and "least verbose to use". So seems like contempory/good-practice use of Hibernate 3.6.

I can load objects from a MySQL database with the following code extracts, but persisted objects are not being flushed/synchronised/saved to the database:

From within Struts 1.0 Action class:

InitialContext ctx = new InitialContext();   
EntityManagerFactory emf = (EntityManagerFactory)ctx.lookup("java:/MyEntityManagerFactory");

'emf' is then passed to a method with my DAO class that is summarised below:

@PersistenceContext(unitName="purejetJPA") EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
exampleMethodInMyCustomDAOClass() {
EntityManager em = emf.createEntityManager();
em.find(MyCustomEntity.class, 542);  // works successfully
em.persist(newInstanceOfMyCustomEntity);          // this executes ok and generates an ID
                                                  // however the entity is not saved to database upon completion 
}

Contents of persistence.xml:

    <?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="myPersistanceUnitName" transaction-type="JTA">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:/MySqlDS</jta-data-source>
                <class>my.custom.entity.Classes</class>
                <properties>
            <property name="jboss.entity.manager.factory.jndi.name" value="java:/MyEntityManagerFactory"/>
            <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/>
            <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
            <property name="hibernate.connection.autocommit" value="false"/>
            <property name="hibernate.current_session_context_class" value="jta"/>
        </properties>   
    </persistence-unit>
</persistence>

The Hibernate EntityManager documentation has a very limited description of how to achieve CMT:


Our entity manager/transaction management idiom for CMT and EJB3 container-use is reduced to this:
//CMT idiom through injection
@PersistenceContext(name="sample") EntityManager em;


And this jboss.org article says:


Transaction demarcation with EJB/CMT

Our goal really is to remove any transaction demarcation code from the data access code:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void doSomeWork() {

// Do some work
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
}

My questions:

  1. In the line "actory.getCurrentSession().load(…);", what type is "factory", and how do I create it? Is it Hibernate.SessionFactory? Or a Jboss or HTTP session?

  2. In the line "@PersistenceContext(name="sample") EntityManager em;" what is "name" referring to? I found an example on a forum of something using "unitName" instead of "name". Is this line how I first declare the EntityManager object that I use to call .persist() .find() etc? (and therefore my code that creates an EntityManagerFactory is not needed)

  3. Should I consider researching and using "Java Context and Dependency Injection" (CDI)?

Any help much appreciated. Please let me know what other bits of code or config files I should supply


Update:

If I don't use EntityManagerFactory, and retrieve an EntityManager instead using @PersistenceContext, then should something like this code for my "session bean" (the class retriving+saving entities on a per-user-session basis) be the way to do it?

@Stateful
@TransactionManagement(value=TransactionManagementType.BEAN)
public class X implements IX {

@PersistenceContext(unitName="MySQL", type=PersistenceContextType.EXTENDED)
private EntityManager em;

@Resource
private UserTransaction tx;

public void doStuff() {
   tx.begin();
   em.joinTransaction();
   em.find(myEntity);
   em.perrsist(myEntity);
   tx.commit();
}

If this is on the right track, what is needed in persistence.xml? From all my reading of doco and the web I'm not sure which of these might be required:

<property name="jboss.entity.manager.factory.jndi.name" value="java:/MyEntityManagerFactory"/>
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.current_session_context_class" value="jta"/> 

Best Answer

1) The answer is in the same document you linked :-)

In the examples above you can see access to the SessionFactory. How do you get access to the factory everywhere in your code? Again, if you run in a Java EE environment, or use an embedded service in JSE, you could simply look it up from JNDI, where Hibernate can bind it on startup. Another solution is to keep it in a global static singleton after startup.

2) The name is a reference to the name specified in the tag persistence-unit from your persistence.xml. In your case, it's myPersistanceUnitName.

3) Yes, if you understand that it's not meant to replace anything, it's meant to facilitate the consumption of "beans" (which may be resources from the AS).

Also note that in order to get a managed EntityManager (with benefits), you should not get the EntityManagerFactory from the AS. You should get the EM itself. Like you mentioned, it's like this:

@PersistenceContext(name="myPersistanceUnitName") EntityManager em;

It will work only in things which are managed by the AS, like Servlets, other CDI beans, EJB, ... Not sure if Struts Action classes would get it injected :-)