I have a simple web application using JTA + JPA along with Spring (version 4). I am doing a XA transaction using Atomikos JTA transaction Manager. This transaction involves inserting EmployeeA entity into DatabaseA and another EmployeeB entity into another DatabaseB.
Strangely, the application does not throw any error but no enteries are inserted into the Databases.
public class JtaTest {
public static void main( String[] args ) {
JtaTest test = new JtaTest();
test.testMandatory();
}
private static void testMandatory() {
final SpringContextUtil util = SpringContextUtil.instance();
ApplicationContext ctx = util.getApplicationContext();
final JtaEmployeeService employeeService = (JtaEmployeeService)ctx.getBean("jtaEmployeeService");
EmployeeA a = new EmployeeA();
a.setName("emp-a-1");
a.setAge(30);
EmployeeB b = new EmployeeB();
a.setName("emp-b-1");
a.setAge(31);
try {
employeeService.persistEmployees(a, b);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("success");
}
}
The service class implementation:
@Service("jtaEmployeeService")
public class JtaEmployeeServiceImpl implements JtaEmployeeService {
@Autowired
@Qualifier("employeea")
private GenericDao<Integer, EmployeeA> employeeADao;
@Autowired
@Qualifier("employeeb")
private GenericDao<Integer, EmployeeB> employeeBDao;
@Override
@Transactional( propagation=Propagation.REQUIRED, readOnly=false, isolation=Isolation.DEFAULT, rollbackFor=Exception.class)
public void persistEmployees(EmployeeA employeeA, EmployeeB employeeB) throws Exception {
employeeADao.save(employeeA);
employeeBDao.save(employeeB);
System.out.println("Saving employee A and employee B ");
}
}
The DAO implementation and interfaces:
@Repository("employeeb")
public class EmployeeBDaoImpl extends AbstractJTADaoDatabaseB implements
GenericDao<Integer, EmployeeB> {
@Override
public void save(EmployeeB entity) {
super.persist(entity);
}
}
@Repository("employeea")
public class EmployeeADaoImpl extends AbstractJTADaoDatabaseA implements
GenericDao<Integer, EmployeeA> {
@Override
public void save(EmployeeA entity) {
super.persist(entity);
}
}
public abstract class AbstractJTADaoDatabaseB {
@PersistenceContext(unitName = "persistenceUnitB")
@Qualifier("myManager")
private EntityManager entityManager;
public void persist(Object entity) {
entityManager.persist(entity);
// entityManager.flush();
}
protected EntityManager getEntityManager() {
return entityManager;
}
}
public abstract class AbstractJTADaoDatabaseA {
@PersistenceContext(unitName = "persistenceUnitA")
@Qualifier("myManager")
private EntityManager entityManager;
public void persist(Object entity) {
entityManager.persist(entity);
// entityManager.flush();
}
protected EntityManager getEntityManager() {
return entityManager;
}
}
Servlet-Context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Enables annotaion driven transactional support on springs -->
<!-- <tx:annotation-driven transaction-manager="hTransactionManager"/> -->
<!-- <tx:jta-transaction-manager transaction-manager="jtaTransactionManager"/> -->
<tx:annotation-driven transaction-manager="jtaTransactionManager"/>
<!-- list the packages which are annotated with springs annotaions like @controller, @repository, @service, @component only -->
<context:component-scan base-package="com.goraksh.spring.tutorial.controller.business, com.goraksh.spring.tutorial.controller.rest, com.goraksh.spring.tutorial.service, com.goraksh.spring.tutorial.dao" />
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:application.properties"/>
</beans:bean>
<!-- <beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.annotation.AnnotationSessionFactoryBean"> -->
<beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</beans:prop>
<!-- <beans:prop key="hibernate.current_session_context_class">thread</beans:prop> -->
<beans:prop key="hibernate.show_sql">${hibernate.show_sql}</beans:prop>
<beans:prop key="hibernate.format_sql">${hibernate.format_sql}</beans:prop>
<beans:prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</beans:prop>
<beans:prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</beans:prop>
<beans:prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</beans:prop>
<beans:prop key="net.sf.ehcache.configurationResourceName">${net.sf.ehcache.configurationResourceName}</beans:prop>
</beans:props>
</beans:property>
<!--<beans:property name="annotatedClasses"> -->
<!-- packagesToScan is meant to give the package where the javax.persistence.Entity classes are stored , recognised via their annotations-->
<beans:property name="packagesToScan">
<beans:list>
<beans:value>com.goraksh.spring.tutorial.model</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="${jdbc.driverClassName}"/>
<beans:property name="url" value="${jdbc.url}"/>
<beans:property name="username" value="${jdbc.username}"/>
<beans:property name="password" value="${jdbc.password}"/>
</beans:bean>
<beans:bean id = "hTransactionManager" class = "org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name = "sessionFactory" ref = "sessionFactory" />
</beans:bean>
<!-- Jata transactions with Atomikos -->
<beans:bean id="dataSourceA" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<beans:property name="uniqueResourceName"><beans:value>DataSourceA</beans:value></beans:property>
<beans:property name="xaDataSourceClassName"><beans:value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</beans:value></beans:property>
<beans:property name="xaProperties">
<beans:props>
<beans:prop key="databaseName">traningdb</beans:prop>
<beans:prop key="serverName">localhost</beans:prop>
<beans:prop key="port">3306</beans:prop>
<beans:prop key="user">root</beans:prop>
<beans:prop key="password">root</beans:prop>
<beans:prop key="url">jdbc:mysql://localhost:3306/traningdb</beans:prop>
</beans:props>
</beans:property>
<beans:property name="minPoolSize"><beans:value>1</beans:value></beans:property>
</beans:bean>
<beans:bean id="dataSourceB" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<beans:property name="uniqueResourceName"><beans:value>DataSourceB</beans:value></beans:property>
<beans:property name="xaDataSourceClassName"><beans:value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</beans:value></beans:property>
<beans:property name="xaProperties">
<beans:props>
<beans:prop key="databaseName">traningdb2</beans:prop>
<beans:prop key="serverName">localhost</beans:prop>
<beans:prop key="port">3306</beans:prop>
<beans:prop key="user">root</beans:prop>
<beans:prop key="password">root</beans:prop>
<beans:prop key="url">jdbc:mysql://localhost:3306/traningdb2</beans:prop>
</beans:props>
</beans:property>
<beans:property name="minPoolSize">
<beans:value>1</beans:value>
</beans:property>
</beans:bean>
<beans:bean id="entityManagerFactoryA" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<beans:property name="persistenceXmlLocation">
<beans:value>classpath:META-INF/persistence.xml</beans:value>
</beans:property>
<beans:property name="persistenceUnitName" value="persistenceUnitA" />
<beans:property name="dataSource" ref="dataSourceA" />
<beans:property name="jpaVendorAdapter">
<beans:bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<beans:property name="showSql" value="true" />
<beans:property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="entityManagerFactoryB" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<beans:property name="persistenceXmlLocation">
<beans:value>classpath:META-INF/persistence.xml</beans:value>
</beans:property>
<beans:property name="persistenceUnitName" value="persistenceUnitB" />
<beans:property name="dataSource" ref="dataSourceB" />
<beans:property name="jpaVendorAdapter">
<beans:bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<beans:property name="showSql" value="true" />
<beans:property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<beans:property name="forceShutdown" value="false" />
<!-- <beans:property name="startupTransactionService" value="false" /> -->
</beans:bean>
<beans:bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<beans:property name="transactionTimeout" value="300" />
</beans:bean>
<beans:bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"
depends-on="atomikosTransactionManager,atomikosUserTransaction">
<beans:qualifier value="myManager"/>
<beans:property name="transactionManager" ref="atomikosTransactionManager" />
<beans:property name="userTransaction" ref="atomikosUserTransaction" />
<beans:property name="allowCustomIsolationLevels" value="true" />
</beans:bean>
</beans:beans>
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
<persistence-unit name="persistenceUnitA" >
<class>com.goraksh.spring.tutorial.model.EmployeeA</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
</properties>
</persistence-unit>
<persistence-unit name="persistenceUnitB" >
<class>com.goraksh.spring.tutorial.model.EmployeeB</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
</properties>
</persistence-unit>
</persistence>
Any help is highly solicited.
After some additional tuning: 1. Removing all hibernate ( jpa properties ) from persistence.xml to the servlet-context.xml.
Added jpaProperties to EntityManagerFactoryA and EntityManagerFactoryB
Servlet-context.xml
Persistence.xml