Spring IllegalStateException: A JTA EntityManager cannot use getTransaction() - java

So after a big refactoring project, I am left with this exception and am unsure as how to correct it. It's dealing with some code that I did not write and I am unfamiliar with how it all works. There are other questions out there dealing with this exception, but none seem to fit my situation.
The class which uses EntityManager is SpecialClaimsCaseRepositoryImpl:
package com.redacted.sch.repository.jpa;
//Imports
#Repository
public class SpecialClaimsCaseRepositoryImpl extends SimpleJpaRepository<SpecialClaimsCaseDto, SpecialClaimsCaseDto.Id> implements SpecialClaimsCaseRepository{
#PersistenceContext(unitName = "schManager")
private EntityManager em;
//Some autogenerated methods
public void setEntityManager(EntityManager em) {
this.em = em;
}
public EntityManager getEntityManager() {
return em;
}
}
Persistence.xml:
<persistence 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"
version="1.0">
<persistence-unit name="schManager">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/SCH_DS</jta-data-source>
<class>com.redacted.sch.domain.model.SpecialClaimsCaseDto</class>
<properties>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.dialect" value="com.bcbsks.hibernate.dialect.DB2Dialect" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.generate_statistics" value="false" />
<property name="hibernate.jdbc.use_scrollable_resultset" value="true" />
</properties>
</persistence-unit>
</persistence>
sch_model_spring.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.redacted.repository.jpa,
com.redacted.sch.domain.model,
com.redacted.sch.repository.jpa,
com.redacted.sch.service,
com.redacted.sch.service.impl"/>
<tx:annotation-driven />
<tx:jta-transaction-manager />
<!-- Data source used for testing -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
<property name="url" value="jdbc:db2:redacted.redacted.com" />
<property name="username" value="redacted" />
<property name="password" value="redacted" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="schManager" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
And here's my project structure:
>
Here's a portion of the stack trace, with the full trace at this fpaste
Caused by: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
at org.hibernate.ejb.AbstractEntityManagerImpl.getTransaction(AbstractEntityManagerImpl.java:985)
at org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:67)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:380)
... 80 more
I'm a total noob here, so if any other information is needed just ask and I'll update.
Thanks for all the help!

The problem is your configuration. You have hibernate configured for JTA.
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
Whereas you are using local transactions instead of distributed transactions.
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:380)
You have 2 possible solutions
remove the JpaTransactionManager and replace it with a JTA transaction manager
remove the remove the hibernate.transaction.manager_lookup_class from the hibernate settings.
If you don't really need distributed transactions option 2 is the easiest, if you need distributed transactions simply adding <tx:jta-transaction-manager /> will setup a proper JTA tx manager for your environment. Remove the definition for the JpaTransactionManager.
Update:
Your configuration is flawed in 2 ways.
Your EntityManager configuration already contains a jndi lookup for the datasource, which you override in your applicationContext by configuring a local datasource
You have both a <tx:jta-transaction-manager /> and JpaTransactionManager which one do you want to use? At the moment the latter is overriding the first one.
Create 2 seperate configurations one for local testing and one for production using JTA en JNDI lookups. (Preferable your testing code only overrides the beans necessary).

Use WebSphereTransactionManagerLookup for the transaction manager lookup in Hibernate
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereTransactionManagerLookup" />
and remove your current transaction manager and replace it with the WebSphereUowTransactionManager.
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
for your transaction manager lookup in Spring.
See IBM Websphere and Spring docs
for more in depth documentation.

Related

Spring + HibernateTemplate + AOP for transaction mamangement not working

I am using simple "helloWorld"ish application to learn Spring,Hibernate and transaction management using AOP. But is not working as expected. I am getting exception in transaction management. Details as follows :-
Spring version 4.3.8
Hibernate version 5.2.10
HSQL DB version 2.3.4
Spring.xml look as follows
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="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-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" mode="aspectj"
transaction-manager="transactionManager" />
<!-- THIS IS COMMENTED. Without commenting same result. I TRIED USING HibernateTransactionManager. still got same result. -->
<!--
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:C:/ProjectRelated/softwares/hsqldb-2.3.4/hsqldb/data/FirstFile"/>
<property name="username" value="sa"/>
<property name="password" value="sys"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" >
<array>
<value>com.kaushik.winnersoft.data</value>
</array>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<bean id="customerDAO" class="com.kaushik.winnersoft.dao.CustomerDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<bean id="customerManager" class="com.kaushik.winnersoft.CustomerManagerImpl">
<property name="customerDAO" ref="customerDAO"></property>
</bean>
DAOImpl class is
public class CustomerDAOImpl implements CustomerDAO {
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
#Override
#Transactional
public void create(Customer customer) {
System.out.println("in dao creating");
hibernateTemplate.save(customer);
System.out.println("in dao creating done");
}
I get output as follows
Doing
in dao creating
Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1165)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:643)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:640)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:359)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:326)
at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:640)
at com.kaushik.winnersoft.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)
at com.kaushik.winnersoft.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:20)
at com.kaushik.winnersoft.SpringTest.main(SpringTest.java:14)
Answer
Based on the coments given below by M. Denium; I did following changes and it worked.
1) Used HibernateTransactionManager instead of DataSourceTransactionManager
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
2) In removed mode="aspectj"
so it looks like
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
I see to problems with your configuration
You are not using the proper PlatformTransactionManager
Unless you are using load- or compile time weaving I doubt the mode="aspectj" is really correct.
First you need to use the PlatformTransactionManager that supports your persistence technology, as you are using Hibernate 5, you need to use the org.springframework.orm.hibernate5.HibernateTransactionManager instead of the DataSourceTransactionManager. (the latter is for application that only do plain JDBC transactions).
From the <tx:annotation-driven /> remove the mode="aspectj" as I suspect you are actually not using full blown AspectJ but are relying on plain Spring to do this for you.
Pro Tip: Instead of using HibernateTemplate, which isn't recommended anymore since Hibernate 3.0.1, just use plain SessionFactory with getCurrentSession instead. This will allow you to write plain Hibernate daos. As suggested in the reference guide.

Spring 4 and Hibernate 4/c3p0 with Entity Manager don't start

I create the configuration of Spring + JPA/Hibernate/c3p0 on this way:
Spring-Servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<context:component-scan base-package="com.nassoft.erpweb"/>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
<mvc:annotation-driven />
<mvc:interceptors>
<bean class="com.nassoft.erpweb.login.interceptor.AuthenticatorInterceptor" />
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.nassoft.erpweb.*" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/nsm_erp" />
<property name="user" value="root" />
<property name="password" value="1234" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="maxStatements" value="50" />
<property name="idleConnectionTestPeriod" value="3000" />
<property name="loginTimeout" value="300" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
<bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
</beans>
I'm not using persistence.xml because I read in some places its not necessary in Spring 4 with Hibernate.
When I start the server it still loading and don't start in 45s (nor 180s) in Tomcat7.
I create a factory of EntityManager to use in my project:
package com.nassoft.erpweb.factory;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceUnit;
public class ConnectionFactory {
#PersistenceUnit
private static EntityManagerFactory entityManagerFactory;
public static EntityManager getEntityManager(){
if (entityManagerFactory == null){
entityManagerFactory = Persistence.createEntityManagerFactory("ERPWeb");
}
return entityManagerFactory.createEntityManager();
}
}
I think my configuration is not correct, but I don't found any places with a good text about it.
Can someone help-me?
Edited.
Problem solved!
First: I applied Dependency Injection in each controller to bring the DAOs with IoC.
Second: I use the annotation #Repository to create a repository in each DAO that will receive my databases methods.
Third: I created the EntityManage in this way for each DAO:
#PersistenceContext
private EntityManager manager;
This is not a complete answer. I"m just pointing you to a direction.
Spring cannot find the ConnectionFactory class of yours, so it will not inject the entityManagerFactory. Its not required for you to again create a singleton for passing the entityManager, so no ConnectionFactory class is required. Spring will do it for you by injecting into the DAO or Controller etc., for example you have the following DAO that gets the data.
#Service
public class SomeDAO {
#AutoWire -- i'm not sure what you call for the entityManager.
private static EntityManagerFactory entityManagerFactory;
}
There is more info here. Instead of #autowiring he is using manual injection. I your case ,you can try with autowiring.
Also make sure that you have these classes in the <spring:component-scan /> path of the application context file or else the spring wont be able to recognize and inject the entity manager.

override spring batch admin to use mysql database

I am trying to use mysql database instead of default HSQL in spring batch admin. For that as per documentation
http://docs.spring.io/spring-batch-admin/reference/reference.xhtml and Using jndi datasource with spring batch admin
I copied env-context.xml to src/main/resources/META-INF/batch/override/manager/env-context.xml and changed its configuration value from
<value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>
to
<value>classpath:batch-mysql.properties</value>
Below is my full configuration.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Use this to set additional properties on beans at run time -->
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
<value>classpath:batch-default.properties</value>
<value>classpath:batch-mysql.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
</beans>
I also tried coping data-source-context.xml to same folder and changing its configurations to mysql
<?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:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/batch" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="testWhileIdle" value="true"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Initialise the database if enabled: -->
<jdbc:initialize-database data-source="dataSource" enabled="false" ignore-failures="DROPS">
<jdbc:script location="classpath*:/org/springframework/batch/core/schema-drop-mysql.sql"/>
<jdbc:script location="classpath:/org/springframework/batch/core/schema-mysql.sql"/>
<jdbc:script location="classpath:/business-schema-mysql.sql"/>
</jdbc:initialize-database>
</beans>
But it still using hsql database? How to override default configuration to use mysql database ?
You shouldn't replace the <value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>. Instead, pass in an environment variable ENVIRONMENT set to mysql. That should cause all the appropriate components to pick up the correct database. You can read more about that feature here: http://docs.spring.io/spring-batch-admin/reference/infrastructure.html#Environment_Settings
If you want to try using just annotations without any xml configurations - try this
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.MYSQL);
It is working. My code is available here - http://github.com/sidnan/spring-batch-example.
I was able to get the connection working with above approach with below steps
First, I copied env-context.xml to src/main/resources/META-INF/batch/override/manager/env-context.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">`
<!-- Use this to set additional properties on beans at run time -->
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
<value>classpath:batch-default.properties</value>
<value>classpath:batch-${ENVIRONMENT:sqlserver}.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
</beans>
After that, put the following entries in batch-sqlserver.properties of sql server under resources as
# Default placeholders for database platform independent features
batch.remote.base.url=http://localhost:8080/spring-batch-admin-sample
# Non-platform dependent settings that you might like to change
batch.jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
batch.jdbc.url=jdbc:sqlserver://localhost;databaseName=batchrepo
batch.jdbc.user=la
batch.jdbc.password=la
atch.jdbc.testWhileIdle=true
batch.data.source.init=false
batch.jdbc.validationQuery=
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer
batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
batch.database.incrementer.parent=columnIncrementerParent
batch.grid.size=2
batch.jdbc.pool.size=6
batch.verify.cursor.position=true
batch.isolationlevel=ISOLATION_SERIALIZABLE
batch.initializer.enabled=false
Since my tables were already created in the database, I skipped these entries:
#batch.drop.script=/org/springframework/batch/core/schema-drop-sqlserver.sql
#batch.schema.script=/org/springframework/batch/core/schema-sqlserver.sql
#batch.business.schema.script=business-schema-sqlserver.sql
Finally, with batch.initializer.enabled=false, I was finally able to make the connection.
I can monitor the job as well as lunch the new jobs in my admin application. These launched jobs also appear in the DB.

Spring ApplicationContext Beans-wiring

I have two ApplicationContexts for my project (very huge project).
One old xml with data
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="autodetect">
</beans>
now I need to add my other project applicatinContext to it or any other way so that none of the modules will be impacted
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="positionResponsesDAO"
class="com.xxx.modules.worklist.DAO.Impl.PositionResponsesDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="positionDAO"
class="com.xxx..modules.worklist.DAO.Impl.PositionDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="nextActionDAO"
class="com.xxx..modules.worklist.DAO.Impl.NextActionDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean>
....... few more
</bean>
<bean id="workOrderManager" class="com.xxx.modules.worklist.action.manager.impl.WorkOrderManagerImpl">
<property name="positionDO" ref="positionDO" />
<property name="moveWorkOrderDO" ref="moveWorkOrderDO" />
<property name="nextActionDO" ref="nextActionDO" />
<property name="positionDAO" ref="positionDAO" />
<property name="moveResponsesDAO" ref="moveResponsesDAO" />
<property name="moveWorkOrderDAO" ref="moveWorkOrderDAO" />
<property name="nextActionDAO" ref="nextActionDAO" />
<property name="positionResponsesDAO" ref="positionResponsesDAO" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl" value="driverUrl" />
<property name="user" value="MCMGR" />
<property name="password" value="MC123" />
</bean>
</beans>
the first one has Auto-wiring enabled and this one has and needs manual wiring. How can i combined both of them to put into one xml or read two configurations.
I don't see why reading two or more application context files is difficult. The usual Spring idiom is to partition configuration according to layer. I typically have configuration for persistence, service, web, etc. If it's a web application, I just add all of them in using the ContextLoaderListener. You can specify as many configuration files as you need.
I would consider one huge configuration file a liability in the same way that I would look down on one huge class for everything. Decomposition is a computer science fundamental. I'd recommend partitioning your configuration.
Mixing annotation-based and XML-based configuration isn't a problem, either.
You'll only have an issue if the two configurations overlap. You'll have to remove one or the other for the conflicted beans.
You can use the <import/> tag. See http://forum.springsource.org/showthread.php?41811-Application-Context-and-include

Spring / JTA / JPA unit test : Rollback not working

I am trying to test an entity EJB3 with Spring.
The EJB itself does not uses Spring and I would like to keep duplications of the production JPA configuration minimal (ie not duplicating persistence.xml for exemple).
My unit tests seems to work but even though my unit tests should be transactionnal, data is persisted between the various test methods ...
Here is my entity :
package sample;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity
public class Ejb3Entity {
public Ejb3Entity(String data) {
super();
this.data = data;
}
private Long id;
private String data;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
My unit test :
package sample;
import static org.junit.Assert.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/appContext.xml"})
#Transactional
public class Ejb3EntityTest {
#PersistenceContext
EntityManager em;
#Before
public void setUp() throws Exception {
Ejb3Entity one = new Ejb3Entity("Test data");
em.persist(one);
}
#Test
public void test1() throws Exception {
Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult();
assertEquals(Long.valueOf(1l), count);
}
#Test
public void test2() throws Exception {
Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult();
assertEquals(Long.valueOf(1l), count);
}
}
and my appContext.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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<bean id="dataSource" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:unittest;DB_CLOSE_DELAY=-1" />
<property name="user" value="" />
<property name="password" value="" />
<property name="transactionManager" ref="jotm" />
</bean>
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitPostProcessors">
<bean class="sample.JtaDataSourcePersistenceUnitPostProcessor">
<property name="jtaDataSource" ref="dataSource" />
</bean>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="database" value="H2" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JOTMTransactionManagerLookup" />
<entry key="hibernate.transaction.auto_close_session" value="false" />
<entry key="hibernate.current_session_context_class" value="jta" />
</map>
</property>
</bean>
</beans>
When I run my test, test2 fails because it finds 2 entity where I expected only one (because the first one should have been rollbacked ...)
I have tried a lot of different configurations and this one seems to be the most comprehensive I can get ... I have no other ideas. Do you ?
When I was trying to integrate JOTM and Hibernate, I eventually ended up having to code my implementation of ConnectionProvider. Here is what it looks like right now: http://pastebin.com/f78c66e9c
Then you specify your implementation as the connection privider in hibernate properties and transactions magically start to work.
The thing is that the default connection provider calls getConnection() on the datasource. In you own implementation you call getXAConnection().getConnection(). This makes the difference
I managed to make it work using Bitronix instead of JOTM. Bitronix provides a LrcXADataSource that allows a non XA database to participate in the JTA transaction.
I think the issues were that H2 is not XA compliant and the enhydra StandardXADataSource does not make it magically so (I also ended using HSQLDB but that is unrelated to the issue).
Here is my spring context that works :
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Bitronix Transaction Manager embedded configuration -->
<bean id="btmConfig" factory-method="getConfiguration"
class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm" />
<property name="journal" value="null" />
</bean>
<!-- create BTM transaction manager -->
<bean id="BitronixTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource"
destroy-method="shutdown" />
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="BitronixTransactionManager" />
<property name="userTransaction" ref="BitronixTransactionManager" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<!-- DataSource definition -->
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource"
init-method="init" destroy-method="close">
<property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource" />
<property name="uniqueName" value="unittestdb" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="3" />
<property name="allowLocalTransactions" value="true" />
<property name="driverProperties">
<props>
<prop key="driverClassName">org.hsqldb.jdbcDriver</prop>
<prop key="url">jdbc:hsqldb:mem:unittestdb</prop>
<prop key="user">sa</prop>
<prop key="password"></prop>
</props>
</property>
</bean>
<!-- Entity Manager Factory -->
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="database" value="HSQL" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.BTMTransactionManagerLookup" />
<entry key="hibernate.transaction.auto_close_session" value="false" />
<entry key="hibernate.current_session_context_class" value="jta" />
</map>
</property>
</bean>
Edit: (Sorry, seems I was only half awake when I wrote this paragraph. Of course you're right, everything should be rolled back by default.)
You could check what the transaction manager is really doing, for example by enabling debug output for it.
Assuming log4j:
log4j.logger.org.springframework.transaction=DEBUG
The transaction manager gives you very nice log output about created and joined transactions, and also about commits and rollbacks. That should help you find out what isn't working with your setup.
Add #Rollback annotation (from org.springframework.test.annotation), just after the #Transactional annotation as mentioned in the spring documentation.
#Rollback is a test annotation that is used to indicate whether a test-
managed transaction should be rolled back after the test method has
completed.
Consult the class-level Javadoc for
org.springframework.test.context.transaction.TransactionalTest-
ExecutionListener for an explanation of test-managed transactions.
When declared as a class-level annotation, #Rollback defines the default
rollback semantics for all test methods within the test class hierarchy. When
declared as a method-level annotation, #Rollback defines rollback semantics
for the specific test method, potentially overriding class-level default
commit or rollback semantics.
As of Spring Framework 4.2, #Commit can be used as direct replacement for
#Rollback(false).
Warning: Declaring #Commit and #Rollback on the same test method or on the
same test class is unsupported and may lead to unpredictable results.
This annotation may be used as a meta-annotation to create custom composed
annotations. Consult the source code for #Commit for a concrete example.

Categories

Resources