Hi, We use JTA and JPA 2.0 with multiple persistence units in Spring. Our JTA is supplied from the Glassfish container. In the example below I show two different datasources/persistence units/entity manager factories where in fact we have quite a few more than that in practice. But the principles are the same.
Each persistence unit is defined in the persistence.xml normally and they bind to JTA/XA datasources in the Glassfish environment using JNDI (of course you would get these through your own JTA manager, e.g. Atomikos, and some of the other configuration would be different too);
Code:
<persistence-unit name="onePU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/oneDB</jta-data-source>
<class>my.classes.here</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.default_schema " value="oneSchema" />
<!-- you will need to definitely change this property below -->
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup" />
<!-- and possibly many of these others -->
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.generate_statistics" value="true" />
</properties>
</persistence-unit>
<persistence-unit name="twoPU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/twoDB</jta-data-source>
<class>my.other.classes.here</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.default_schema " value="twoSchema" />
<!-- other properties as above omitted for brevity -->
</properties>
</persistence-unit>
The database mapping classes are annotated with the standard JPA annotations. Note the DB schema is specified in the persistence.xml above not in the database mapping classes! This allows simple integration test configurations using a single in-memory HSQLDB (doesn't support schemas) and so forth where a different persistence.xml is used that doesn't specify the schema or whatever is needed in the particular test configuration.
The material point here is that nearly all the database configuration is now confined to either the persistence.xml or inside the Container (e.g. location/username/password/XA driver class etc). Meaning the rest of the application is now entirely independent of most of the database semantics.
In the web.xml these persistence units are made available as JNDI resources and Persistence Units;
Code:
<persistence-unit-ref>
<persistence-unit-ref-name>persistence/onePU</persistence-unit-ref-name>
<persistence-unit-name>onePU</persistence-unit-name>
</persistence-unit-ref>
<persistence-unit-ref>
<persistence-unit-ref-name>persistence/twoPU</persistence-unit-ref-name>
<persistence-unit-name>twoPU</persistence-unit-name>
</persistence-unit-ref>
<persistence-context-ref>
<persistence-context-ref-name>persistence/onePU</persistence-context-ref-name>
<persistence-unit-name>onePU</persistence-unit-name>
</persistence-context-ref>
<persistence-context-ref>
<persistence-context-ref-name>persistence/twoPU</persistence-context-ref-name>
<persistence-unit-name>twoPU</persistence-unit-name>
</persistence-context-ref>
Now, the trick. In the Spring application context, this is my sum total of all the database configuration (!);
Code:
<tx:annotation-driven />
<tx:jta-transaction-manager />
<bean id="pabpp" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<jee:jndi-lookup id="onePU" jndi-name="persistence/onePU" />
<jee:jndi-lookup id="twoPU" jndi-name="persistence/twoPU" />
The JNDI lookup of the persistence unit JNDI names means there is are two EntityManagerFactories in the app context called "onePU" and "twoPU". The PersistenceAnnotationBeanPostProcessor finds these and configures them such that on my DB service layer classes I am able inject the correct entity manager as follows;
Code:
@PersistenceContext(unitName = "onePU")
private EntityManager em;
Because the unitName in the @PersistenceContext matches the ID of the JNDI lookup entity manager factory Spring knows where to find the injected EntityManager. Although I'm fairly sure this facility is provided via the PersistenceAnnotationBeanPostProcessor in fact I'm not entirely sure whether that bean is necessary anymore (I used to inject the JNDI names directly into a property of the PersistenceAnnotationBeanPostProcessor and not have any external jndi-lookup, until I needed declarative access to the EntityManagerFactories elsewhere).
Of course, I'm using Spring's @Transactional annotation on the methods of the DB service layer classes with appropriate isolation, propagation, rollback rules, and other configuration in the annotation as necessary;
Code:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Message saveMessage(Message message) { ... }
@Transactional(propagation = Propagation.MANDATORY)
public List<Message> activateMessagesForCurrentConnection(ConnectionDetails connectionDetails) { ... }
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Message getMessageDetails(UUID messageId) { ... }
Then in my web.xml I also put this filter;
Code:
<!-- ONE -->
<filter>
<filter-name>openEntityManagerInViewFilterEden</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>onePU</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilterEden</filter-name>
<url-pattern>/one/*</url-pattern>
</filter-mapping>
<!-- TWO -->
<filter>
<filter-name>openEntityManagerInViewFilterElec</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>twoPU</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilterElec</filter-name>
<url-pattern>/two/*</url-pattern>
</filter-mapping>
So now if a user opens anything on the path "/one/*" the "onePU" EntityManagerFactory is in scope, and likewise for "/two/*" (it is actually slightly more complex than that, there are paths where both must be in scope, and so forth).
Although we had a difficult path getting this far (understanding the available options through the sparsity of the documentation around a configuration like this), I'm finding the overall solution now quite elegant.
Hope this helps you.