Results 1 to 7 of 7

Thread: Howto JTA + JPA + JDBC in unit tests

Hybrid View

  1. #1
    Join Date
    Mar 2009
    Location
    Germany
    Posts
    18

    Default Howto JTA + JPA + JDBC in unit tests

    Hi,

    I'm struggling a bit because I cannot get JTA transactions to work in a unit test.

    The scenario I'm trying to test is very easy: A DAO inserts a Mail object into a database with JPA. After the insert, the SimpleJdbcTemplate is used to verify that the number of rows in the table has increased.

    Everything works fine when a "normal" transaction manager is used. But with JTA, the SimpleJdbcTemplate (inherited because the test class extends AbstractTransactionalJUnit4SpringContextTests) cannot "see" the added row.

    How can I solve this problem? Thanks in advance for your help!

    Stephan

    I use the following libraries:
    • Spring 2.5.6
    • Bitronix Transaction Manager 1.3.2
    • JPA with Hibernate EntityManager 3.4
    • PostgreSQL 8.3.7 database



    This is the Spring configuration:
    Code:
    	<context:annotation-config />
    	<tx:annotation-driven />
    
    	<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init"
    		destroy-method="close">
    		<property name="className" value="org.postgresql.xa.PGXADataSource" />
    		<property name="uniqueName" value="postgres" />
    		<property name="maxPoolSize" value="5" />
    		<property name="testQuery" value="SELECT 1" />
    		<property name="allowLocalTransactions" value="true" />
    		<property name="driverProperties">
    			<props>
    				<prop key="user">xxx</prop>
    				<prop key="password">xxx</prop>
    				<prop key="serverName">xxx</prop>
    				<prop key="portNumber">5432</prop>
    				<prop key="databaseName">spring_dev</prop>
    			</props>
    		</property>
    	</bean>
    
    	<!--  Bitronix Transaction Manager embedded configuration -->
    	<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
    		<property name="serverId" value="spring-btm" />
    	</bean>
    
    	<!-- create BTM transaction manager -->
    	<bean id="BitronixTransactionManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices"
    		depends-on="btmConfig" destroy-method="shutdown" />
    
    	<!-- Spring JtaTransactionManager -->
    	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    		<property name="transactionManager" ref="BitronixTransactionManager" />
    		<property name="userTransaction" ref="BitronixTransactionManager" />
    	</bean>
    
    	<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    		<property name="databasePlatform" value="${jpa.databasePlatform}" />
    		<property name="database" value="${jpa.database}" />
    	</bean>
    
    	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    		<property name="jpaProperties">
    			<props>
    				<prop key="hibernate.hbm2ddl.auto">create</prop>
    				<prop key="hibernate.ejb.naming_strategy">${hibernate.ejb.naming_strategy}</prop>
    				<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.BTMTransactionManagerLookup
    				</prop>
    				<prop key="hibernate.current_session_context_class">jta</prop>
    			</props>
    		</property>
    	</bean>
    
    	<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    		abstract="true">
    		<property name="transactionManager" ref="transactionManager" />
    		<property name="transactionAttributes">
    			<props>
    				<prop key="*">PROPAGATION_REQUIRED, -Exception</prop>
    			</props>
    		</property>
    	</bean>
    
    	<bean id="myProxy" parent="baseTransactionProxy">
    		<property name="target" ref="mailDao" />
    	</bean>
    
    	<bean id="mailDao" class="org.example.mail.dao.impl.MailDaoImpl" />
    
    	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<constructor-arg ref="dataSource" />
    	</bean>
    </beans>
    And here is the unit test:
    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    public class JpaTest extends AbstractTransactionalJUnit4SpringContextTests {
    	protected static final String MAIL_TABLE = "MAIL";
    	protected MailDao mailDao;
    
    	@PersistenceContext
    	protected EntityManager sharedEntityManager;
    
    	@Before
    	public void init() {
    		mailDao = (MailDao) applicationContext.getBean("mailDao");
    	}
    
    	@Test
    	public void addMail_shouldIncrementNumberOfRows() {
    		int recordsInTableBefore = countRowsInTable(MAIL_TABLE);
    		mailDao.add(createDefaultMail());
    		sharedEntityManager.flush();
    		int rowsInTable = simpleJdbcTemplate.queryForInt("SELECT COUNT(*) FROM " + MAIL_TABLE);
    		assertThat(rowsInTable, is(recordsInTableBefore + 1));
    	}
    
    }

  2. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,624

    Default

    Well I would start by removing the duplicate transaction configuration, either use annotation driver or the tx:advice (i strongly discourage, as does SpringSource, the use of the TransactionalProxyFactoryBean).

    Also you are retrieving the non transactional mail dao yourself in the init method, why? Use the test framework to inject it.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  3. #3
    Join Date
    Mar 2009
    Location
    Germany
    Posts
    18

    Default

    Thanks for your suggestions! I removed the duplicate transaction configuration and only left <tx:annotation-driven /> in my config file. The other entry seemed to be left over from my countless attempts to get it working.

    I also changed the mail DAO retrieval. Now the config file contains <context:component-scan ...> and the DAO is injected via @Autowired in my test case.

    But unfortunately, the problem is still there. I attached a logfile that the test produces. Maybe that can help?

    Thanks, Stephan
    Attached Files Attached Files

  4. #4
    Join Date
    Mar 2009
    Location
    Germany
    Posts
    18

    Default Atomikos Transactions

    I changed my test such that it uses Atomikos Transactions instead of BTM. The result is still the same, but maybe can find more useful information in the logfile that is attached.
    Attached Files Attached Files

  5. #5
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,624

    Default

    Can you post your new configuration...
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  6. #6
    Join Date
    Mar 2009
    Location
    Germany
    Posts
    18

    Default

    Code:
    	<context:annotation-config />
    	<context:component-scan base-package="mycompany.common.mail.dao" />
    	<tx:annotation-driven />
    
    	<bean id="userTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp"
    		init-method="init" destroy-method="shutdownForce">
    		<constructor-arg>
    			<!-- IMPORTANT: specify all Atomikos properties here -->
    			<props>
    				<prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory
    				</prop>
    			</props>
    		</constructor-arg>
    	</bean>
    
    	<bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
    		init-method="init" destroy-method="close" depends-on="userTransactionService">
    		<!-- when close is called, should we force transactions to terminate or not? -->
    		<property name="forceShutdown" value="false" />
    	</bean>
    
    	<bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"
    		depends-on="userTransactionService">
    		<property name="transactionTimeout" value="300" />
    	</bean>
    
    	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"
    		depends-on="userTransactionService">
    		<property name="transactionManager" ref="AtomikosTransactionManager" />
    		<property name="userTransaction" ref="AtomikosUserTransaction" />
    	</bean>
    
    
    	<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    		<property name="databasePlatform" value="${jpa.databasePlatform}" />
    		<property name="database" value="${jpa.database}" />
    	</bean>
    
    	<bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
    		destroy-method="close">
    		<property name="uniqueResourceName" value="oracle" />
    		<property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource" />
    		<property name="maxPoolSize" value="5" />
    		<property name="xaProperties">
    			<props>
    				<prop key="user">user</prop>
    				<prop key="password">password</prop>
    				<prop key="URL">jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:DEV
    				</prop>
    			</props>
    		</property>
    	</bean>
    
    
    	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    		<property name="jpaProperties">
    			<props>
    				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
    				<prop key="hibernate.ejb.naming_strategy">${hibernate.ejb.naming_strategy}</prop>
    				<prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
    				</prop>
    				<prop	key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory</prop>
    				<prop key="hibernate.current_session_context_class">jta</prop>
    			</props>
    		</property>
    	</bean>
    
    </beans>
    The configuration now seems to work, I changed the database from PostgreSQL to Oracle.

    But I still need a different database for automatic unit testing. I tried H2 and Derby which are supposed to support XA, but with both databases I get a deadlock when I try to run the test. I'll post the configuration and logs in the next reply.

    Thanks!
    Stephan
    Last edited by AuroraBorealis; Jul 6th, 2009 at 02:49 AM. Reason: Sorry, pressed "Post reply" to soon ...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •