Updated version available: If you are looking for how JPA can be used in JBoss 5 and Spring 3.x versions, look at "Spring JPA web applications (JTA transactions, JBoss 5)". What you find below is a workaround to use JTA transactions in JBoss 4.x versions.
Here's how one might go about deploying a Spring application in JBoss (4.something) that uses JPA with Hibernate as the provider for persistence and JTA for transaction demarcation.
1. Define the Spring configuration file in the web.xml file
<context-param>
<description>Spring configuration file</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
2. Define the Spring loader in the web.xml file
<listener>
<description>Spring Loader</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3. Define the persistence unit reference in the web.xml file (which in fact has no effect until the Servlet container supports Servlet spec 2.5):
<persistence-unit-ref>
<description>
Persistence unit for the bank application.
</description>
<persistence-unit-ref-name>persistence/BankAppPU</persistence-unit-ref-name>
<persistence-unit-name>BankAppPU</persistence-unit-name>
</persistence-unit-ref>
* Note that this is what enables "<jee:jndi-lookup>" which has been commented out in the below given Spring configuration file.
* For the above to work well, your web.xml should start like this (note the version 2.5):
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
4. Here's the persistence.xml file. Make the changes to the <jta-data-source> as you have defined in your system (for example in a file like JBOSS_HOME/server/default/deploy/bank-ds.xml - See JBOSS_HOME/docs/examples/jca/ for templates).
<?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="BankAppPU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:BankAppDS</jta-data-source>
<properties>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="jboss.entity.manager.factory.jndi.name" value="java:/BankAppPU"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
5. Here's a sample Spring configuration file (applicationContext.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<!-- In a fully J5EE compatible environment, the following xml tag should work in accessing the EMF -->
<!--
<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/BankAppPU"/>
-->
<!-- Hack for JBoss 4.something until full compliance is reached -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="BankAppPU"/>
</bean>
<!-- Let's access the JTA transaction manager of the application server -->
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager"/>
<property name="userTransactionName" value="UserTransaction"/>
</bean>
<!-- Let's define a DAO that uses the EMF -->
<bean id="accountHolderDAO" class="bankapp.dao.AccountHolderDAO">
<property name="emf" ref="entityManagerFactory"/>
</bean>
<!-- This is a service object that we want to make transactional.
You will have an interface implemented (AccountManager) in the class.
-->
<bean id="accountManager" class="bankapp.AccountManagerImpl">
<property name="accountHolderDAO" ref="accountHolderDAO"/>
</bean>
<!-- The transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only transactions -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for execution
of any operation defined by the AccountManager interface -->
<aop:config>
<aop:pointcut id="accountManagerOperation",
expression="execution(* bankapp.AccountManager.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="accountManagerOperation"/>
</aop:config>
</beans>
6. Here's the sample AccountManagerImpl:
public class AccountManagerImpl implements AccountManager {
/** Creates a new instance of AccountManagerImpl */
public AccountManagerImpl() {
}
private AccountHolderDAO accountHolderDAO;
public AccountHolder createAccountHolder(AccountHolder accountHolder) throws BankException {
return accountHolderDAO.create(accountHolder);
}
public AccountHolderDAO getAccountHolderDAO() {
return accountHolderDAO;
}
public void setAccountHolderDAO(AccountHolderDAO accountHolderDAO) {
this.accountHolderDAO = accountHolderDAO;
}
}
7. Here's the sample AccountHolderDAO:
public class AccountHolderDAO {
/** Creates a new instance of AccountHolderDAO */
public AccountHolderDAO() {
}
private EntityManagerFactory emf;
public EntityManagerFactory getEmf() {
return emf;
}
public void setEmf(EntityManagerFactory emf) {
this.emf = emf;
}
public AccountHolder create(AccountHolder newAccountHolder) throws BankException {
try {
// JTA Transaction assumed to have been started by AccountManager (Spring tx advice)
EntityManager em = emf.createEntityManager();
//em.getTransaction().begin(); - Not required
em.persist(newAccountHolder);
//em.getTransaction().commit(); - Not required
return newAccountHolder;
// JTA Transaction will be completed by Spring tx advice
} catch (Exception e) {
throw new BankException("Account creation failed" + e.getMessage(), e);
}
}
}
You will have some other code accessing the Spring bean "accountManager" and invoke the createAccountHolder() with the required parameters. Things should work well.