I'm trying to unit test some persistence code and I run into the problem that the database hangs on to the rows creating in previous tests. I am also using Hibernate Search although this is not reflected in the code I'm sharing here.
I'm using Spring 3.1.x, Hibernate 4.x and HSQLDB 2.3.2
Log
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
Hibernate: insert into UserRole (userRoleId, label) values (default, ?)
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.UserRole#1]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
Hibernate: insert into UserRole (userRoleId, label) values (default, ?)
***First test****
DEBUG - AbstractPlatformTransactionManager.processRollback(843) | Initiating transaction rollback
DEBUG - HibernateTransactionManager.doRollback(496) | Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.User#1],EntityKey[nl.project.model.user.UserRole#1],EntityKey[nl.project.model.user.UserRole#2], EntityKey[nl.project.model.user.UserRole#3]],collectionKeys=[CollectionKey[nl.project.model.user.User.roles#1]]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
DEBUG - HibernateTransactionManager.doBegin(342) | Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - HibernateTransactionManager.doBegin(352) | Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
DEBUG - HibernateTransactionManager.doBegin(413) | Exposing Hibernate transaction as JDBC transaction [com.jolbox.bonecp.ConnectionHandle#73d17d67]
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
Hibernate: select this_.userRoleId as userRole1_92_0_, this_.label as label2_92_0_ from UserRole this_
DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.UserRole#3], EntityKey[nl.project.model.user.UserRole#2], EntityKey[nl.project.model.user.UserRole#1]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
Hibernate: insert into UserRole (userRoleId, label) values (default, ?)
WARN - SqlExceptionHelper.logExceptions(144) | SQL Error: -104, SQLState: 23505
ERROR - SqlExceptionHelper.logExceptions(146) | integrity constraint violation: unique constraint or index violation; UK_O0VIK8LBO8UYMR8WUDN5T21QX table: USERROLE
Code
#Entity
#Immutable
#Table
#NaturalIdCache(region=CacheRegion.NATURAL)
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.USER)
public class UserRole implements Serializable{
public static final String ROLE_ANONYMOUS="ROLE_ANONYMOUS";
public static final String ROLE_USER="ROLE_USER";
public static final String ROLE_PROVENDOR="ROLE_PROVENDOR";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable=false, updatable=false)
private Long id;
#Column(nullable=false,unique=true,updatable=false)
#NaturalId
private String label;
...
}
Code
public class CoreTest extends TestCase {
#Inject
protected SimpleManager<Long> simpleMgr;
public void baseSetup(){
simpleMgr.flush();
simpleMgr.clear();
//after 1st test this contains all created UserRoles
List roles = simpleMgr.getAll(UserRole.class);
UserRole role = new UserRole(UserRole.ROLE_ANONYMOUS);
simpleMgr.save(role);
role = new UserRole(UserRole.ROLE_USER);
simpleMgr.save(role);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/spring/applicationContext.xml"
"classpath:/spring/applicationContext-transaction.xml"})
public class MyManagerTest extends CoreTest{
#Before
public void methodSetup(){
super.baseSetup();
role = new UserRole(UserRole.ROLE_PROUSER);
simpleMgr.save(role);
simpleMgr.save(userMgr.createUser("marc", "marc#gmail.com"));
simpleMgr.flush();
}
#Test
#Transactional
public void test1(){
...
}
#Test
#Transactional
public void test2(){
....
}
Transaction management
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="mainDataSource"/>
</bean>
<bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
[...]
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="hibernateProperties">
<value>
hibernate.connection.driver_class=${jdbc.driverClassName}
hibernate.jdbc.batch_size=30
hibernate.dialect=${hibernate.dialect}
hibernate.connection.autoReconnect=true
hibernate.connection.autoReconnectForPools=true
hibernate.connection.autocommit=false
</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Try adding #Transactional annotation on test class. http://docs.spring.io/spring/docs/2.0.x/api/org/springframework/transaction/annotation/Transactional.html
Try adding transaction interceptor:
<beans xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Extend AbstractTransactionalJunit4SpringContextTests or add #TestExecutionListeners(TransactionalTestExecutionListener.class).
So, I finally got this working by specifying defaultAutoCommit=false on the BoneCPDatasource. AFAIK this is way too low level for this configuration, and therefore possibly wrong. But at least it's working.
Related
All API call are taking long time to respond because, spring is taking long time to return cached instance of singleton bean - transactionManager. Please see log, this behaviour is consistent for each request.
2
014-09-24 08:09:02,239 DEBUG servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/emsp/locations]
2014-09-24 08:09:02,239 DEBUG annotation.RequestMappingHandlerMapping - Looking up handler method for path /locations
2014-09-24 08:09:02,239 DEBUG annotation.RequestMappingHandlerMapping - Did not find handler method for [/locations]
2014-09-24 08:09:02,239 DEBUG servlet.DispatcherServlet - Last-Modified value for [/emsp/locations] is: -1
2014-09-24 08:09:02,240 DEBUG support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'integrationEvaluationContext'
2014-09-24 08:09:02,241 DEBUG support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'endpointLookupService'
2014-09-24 08:09:07,407 DEBUG support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'txManager'
2014-09-24 08:09:07,407 DEBUG hibernate4.HibernateTransactionManager - Creating new transaction with name [com.***.emsp.service.impl.EndpointLookupServiceImpl.getEndpointLocations]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2014-09-24 08:09:07,407 DEBUG hibernate4.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2014-09-24 08:09:07,407 DEBUG hibernate4.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2014-09-24 08:09:07,407 DEBUG internal.LogicalConnectionImpl - Obtaining JDBC connection
2014-09-24 08:09:07,407 DEBUG resourcepool.BasicResourcePool - trace com.mchange.v2.resourcepool.BasicResourcePool#7cfea9ab [managed: 1, unused: 0, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection#6a4d7764)
2014-09-24 08:09:07,407 DEBUG internal.LogicalConnectionImpl - Obtained JDBC connection
2014-09-24 08:09:07,407 DEBUG spi.AbstractTransactionImpl - begin
2014-09-24 08:09:07,408 DEBUG jdbc.JdbcTransaction - initial autocommit status: true
2014-09-24 08:09:07,408 DEBUG jdbc.JdbcTransaction - disabling autocommit
2014-09-24 08:09:07,408 DEBUG hibernate4.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection#3c0b2e6e]
2014-09-24 08:09:07,408 INFO impl.EndpointLookupServiceImpl - EndpointLookupServiceImpl::getEndpointLocations - called (Custom log - after this is almost instantaneous)
If you see these two lines specifically in the above line - there is a 5sec delay - this keeps increasing after a while but comes down once tomcat is restarted.
2014-09-24 08:09:02,241 DEBUG support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'endpointLookupService'
2014-09-24 08:09:07,407 DEBUG support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'txManager'
My spring config
<?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" 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-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.****.emsp" />
<!-- Transaction Manager Declaration -->
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/emsp" />
<property name="user" value="***" />
<property name="password" value="***" />
<!-- C3P0 properties -->
<property name="acquireIncrement" value="1" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="300" />
<property name="idleConnectionTestPeriod" value="3000" />
<!--property name="testConnectionOnCheckout" value="true" /> <property
name="preferredTestQuery" value="select 1;" / -->
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.****.emsp.entity" />
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=update
</value>
</property>
</bean>
</beans>
And i'm using #Transactional annotation for my service APIs involving DB transaction.
Please suggest how to go about debugging this. Also let me know if any another info is required.
Request Handling code:
#Service
public class EndpointLookupServiceImpl implements EndpointLookupService {
private static final Logger logger = Logger
.getLogger(EndpointLookupServiceImpl.class);
#Autowired
EndpointLocationDaoImpl endpointLoctionDao;
#Autowired
SsidDaoImpl ssidDao;
#Override
#Transactional
public EndpointLocationsResp getEndpointLocations(
String xJwtAssertionHeader, Map<String, List<String>> reqParams) {
logger.info("EndpointLookupServiceImpl::getEndpointLocations - called with xJwtAssertionHeader:"
+ xJwtAssertionHeader + " reqParams:" + reqParams);
.....
}
}
Using spring integration as controller for invoking the service:
<int-http:inbound-gateway id="endpointLocById"
request-channel="endpointLocByIdIn"
supported-methods="GET"
path="/locations/{locationId}"
mapped-request-headers="*"
payload-expression="#pathVariables.locationId" >
</int-http:inbound-gateway>
<int:channel id="endpointLocByIdIn"/>
<int:service-activator input-channel="endpointLocByIdIn" expression="#endpointLookupService.getEndpointLocationByLocationId(headers['x-jwt-assertion'], payload)" output-channel="out" />
Take thread dumps just before and after you see the blocking to see what your main thread is doing, Spring loads beans in a single thread, so you should be able to see where it is stuck and debug that even more.
I have a Java EE Application with Spring 3.1.1 and Hibernate 4.1. Now I wanted to speed up things and saw that the bottleneck is the opening + closing of multiple transactions in one request.
Now I removed all #Transactional annotations and created my own OpenSessionInViewFilter, which opens and closes one transaction.
package utils.spring;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class CustomHibernateSessionViewFilter extends OpenSessionInViewFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
//BEGIN TRANSACTION
session.beginTransaction();
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
// COMMIT
sessionHolder.getSession().getTransaction().commit();
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
}
}
Is that a good idea? It seems to work and speeded up things.
Here is my log for the transactions:
http-bio-8080-exec-3 01/03/2013 11:25:20,947 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doGetTransaction | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
http-bio-8080-exec-3 01/03/2013 11:25:20,948 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | getTransaction | Creating new transaction with name [by2.server.service.UserService.loadUserByUsername]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
http-bio-8080-exec-3 01/03/2013 11:25:20,948 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doBegin | Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
http-bio-8080-exec-3 01/03/2013 11:25:21,172 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doBegin | Exposing Hibernate transaction as JDBC transaction [org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler#1e7b64f4[valid=true]]
http-bio-8080-exec-3 01/03/2013 11:25:21,188 | DEBUG | org.hibernate.SQL | logStatement | select userentity_.userID as userID5_ from users userentity_ where userentity_.username=?
connection pool
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>4.1.1.Final</version>
</dependency>
<property name="hibernateProperties">
<value>
hibernate.hbm2ddl.auto=update
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.bytecode.use_reflection_optimizer=false
hibernate.max_fetch_depth=0
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=300
hibernate.c3p0.max_statements=50
hibernate.c3p0.idle_test_period=3000
</value>
</property>
but the open session in view filter seems to close the sessions
finally {
if (!participate) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
But even when I remove the filter, Hibernate doesn't seem to use the pool.
I would say no.
For example, you'll save some product or whatever in the database, and show a success page or redirect, thinking everything has been saved. But the transaction won't be committed yet and could still rollback after you have displayed the success message.
And with Hibernate, the probability of this happening is even bigger, because nothing will be written to the database until flush time, which will happen just before the commit.
Moreover the transaction will live longer than necessary, preventing other transactions to procees in case it has put a lock on rows or tables in the database, resulting in poorer performance and scalability.
What's wrong with the default Spring OpenSessionInViewFilter, which lets the session open, but still uses service-level, short transactions? Why do your requests open multiple transactions? My quess is that you're doing too many calls from the UI layer to the service layer in a single request.
Finally i configured my pool the right way... the solution was not to add some c3p0 properties to my hibernate config, i just had to replace my datasource-bean
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="org.postgresql.Driver" />
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/DBNAME" />
<property name="user" value="xxx" />
<property name="password" value="xxx" />
<!-- Pool properties -->
<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>
runs like a charm now
I am starting a project using Spring 3.2.1.RELEASE, Hibernate 4.1.9.FINAL as jpa provider, spring data 1.2.0.RELEASE for repository and bitronix 2.1.3 as transaction manager. I am new to all of these technologies so I'm sorry if I'm missing some huge point.
I am running a simple unit test to create a User object in a database:
#ContextConfiguration(locations={"classpath:tests-context.xml"})
#Transactional
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
#RunWith(SpringJUnit4ClassRunner.class)
public class UserTest extends AbstractTransactionalJUnit4SpringContextTests
{
#Autowired
protected UserMgmtService userService;
#Test
public void createUserTest()
{
User user = new User();
user.setFirstName("jonny");
user.setLastName("doe");
user.setUsername("user5");
user.setPasswordHash("1238");
user.setEmail("user5#domain.com");
System.out.println("Created user" + user);
User testUser = userService.saveUser(user);
System.out.println("Saved user" + testUser);
Assert.assertEquals("The user should be equal to saved user", user, testUser);
}
}
My test-context.xml used is as follows:
<context:annotation-config/>
<!-- Bitronix Transaction Manager embedded configuration -->
<bean id="btmConfig" factory-method="getConfiguration"
class="bitronix.tm.TransactionManagerServices">
</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="jdbc/MySQL_Test_Repository" />
<property name="minPoolSize" value="0" />
<property name="maxPoolSize" value="5" />
<property name="allowLocalTransactions" value="true" />
<property name="driverProperties">
<props>
<prop key="driverClassName">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306 MySQL_Test_Repository</prop>
<prop key="user">root</prop>
<prop key="password">123654</prop>
</props>
</property>
</bean>
<!-- create BTM transaction manager -->
<bean id="BitronixTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource"
destroy-method="shutdown" />
<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"
depends-on="BitronixTransactionManager">
<property name="transactionManager" ref="BitronixTransactionManager" />
<property name="userTransaction" ref="BitronixTransactionManager" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<!-- AOP configuration for transactions -->
<aop:config>
<aop:pointcut id="userServiceMethods"
expression="execution(* users.service.UserMgmtService.*(..))" />
<aop:advisor advice-ref="transactionAdvice"
pointcut-ref="userServiceMethods" />
</aop:config>
<!-- Transaction configuration -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- bean declaring the property map for the entity manager factory bean -->
<bean id="jpaPropertyMap" class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!-- show the sql code executed -->
<entry key="hibernate.show_sql" value="true"/>
<!-- format the shown sql code -->
<entry key="hibernate.format_sql" value="true"/>
<!-- enables hibernate to create db schemas from classes;
Possible values: validate | update | create | create-drop
with create-drop the schema will be created/dropped for each hibernate sesssion open/close-->
<entry key="hibernate.hbm2ddl.auto" value="update"/>
<!-- sets the hibernate classname for the TransactionManagerLookup;
hibernate wraps and hides the underlying transaction system and needs a reference
to the transaction manager used -->
<entry key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<entry key="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
</map>
</property>
</bean>
<!-- bean declaring the entity manager factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="ruleEditor.persistence"/>
<property name="dataSource" ref="dataSource" />
<property name="jpaPropertyMap" ref="jpaPropertyMap"/>
</bean>
<!-- The location where Spring scans for interfaces implementing the JPARepository
and creates their implementation -->
<jpa:repositories base-package="users.repository" />
<!-- Persistence annotations for post processing -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- User service implementation bean -->
<bean id="userMgmtService" class="users.service.impl.UserMgmtServiceImpl"/>
</beans>
The Repository implementation is a standard JPARepository:
public interface UserRepository extends JpaRepository<User, Long>
{
User findByUsername(String username);
}
And the service implementation only makes calls to the repository:
#Service("userMgmtService")
public class UserMgmtServiceImpl implements UserMgmtService
{
#Autowired
private UserRepository userRepository;
#Override
public User saveUser(User user)
{
User savedUser = userRepository.save(user);
return savedUser;
}
The problem is that when I execute the test, it passes, but no user is created in the database. I thought it might be because of the Rollback behavior so I set the defaultRollback=false in the User test. Here is some information provided by the transaction framework when set to DEBUG that might be relevant:
Running UserTest
2013-02-12 13:01:12 INFO XmlBeanDefinitionReader:315 - Loading XML bean definitionsfrom class path resource [tests-context.xml]
2013-02-12 13:01:12 INFO GenericApplicationContext:510 - Refreshing org.springframework.context.support.GenericApplicationContext#7d95609: startup date [Tue Feb 12 13:01:12 CET 2013]; root of context hierarchy
2013-02-12 13:01:12 INFO GenericApplicationContext:1374 - Bean 'dataSource' of type [class bitronix.tm.resource.jdbc.PoolingDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO GenericApplicationContext:1374 - Bean 'jpaPropertyMap' of type [class org.springframework.beans.factory.config.MapFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO GenericApplicationContext:1374 - Bean 'jpaPropertyMap' of type [class java.util.LinkedHashMap] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO LocalContainerEntityManagerFactoryBean:264 - Building JPA container EntityManagerFactory for persistence unit 'ruleEditor.persistence'
2013-02-12 13:01:13 INFO SchemaUpdate:182 - HHH000228: Running hbm2ddl schema update
2013-02-12 13:01:13 INFO SchemaUpdate:193 - HHH000102: Fetching database metadata
2013-02-12 13:01:13 INFO SchemaUpdate:205 - HHH000396: Updating schema
2013-02-12 13:01:13 INFO TableMetadata:65 - HHH000261: Table found: MySQL_Test_Repository.user
2013-02-12 13:01:13 INFO TableMetadata:66 - HHH000037: Columns: [id, enabled, first_name, username, email, password_hash, last_name]
2013-02-12 13:01:13 INFO TableMetadata:68 - HHH000108: Foreign keys: []
2013-02-12 13:01:13 INFO TableMetadata:69 - HHH000126: Indexes: [id, username, primary]
2013-02-12 13:01:13 INFO SchemaUpdate:240 - HHH000232: Schema update complete
2013-02-12 13:01:13 INFO BitronixTransactionManager:390 - Bitronix Transaction Manager version 2.1.3
2013-02-12 13:01:13 WARN Configuration:649 - cannot get this JVM unique ID. Make sure it is configured and you only use ASCII characters. Will use IP address instead (unsafe for production usage!).
2013-02-12 13:01:13 INFO Recoverer:152 - recovery committed 0 dangling transaction(s) and rolled back 0 aborted transaction(s) on 1 resource(s) [jdbc/MySQL_Test_Repository]
2013-02-12 13:01:14 INFO GenericApplicationContext:1374 - Bean 'entityManagerFactory' of type [class org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO GenericApplicationContext:1374 - Bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' of type [class org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO GenericApplicationContext:1374 - Bean 'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0' of type [class org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO GenericApplicationContext:1374 - Bean 'org.springframework.transaction.config.internalTransactionAdvisor' of type [class org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO JtaTransactionManager:470 - Using JTA UserTransaction: a BitronixTransactionManager with 0 in-flight transaction(s)
2013-02-12 13:01:14 INFO JtaTransactionManager:481 - Using JTA TransactionManager: a BitronixTransactionManager with 0 in-flight transaction(s)
2013-02-12 13:01:14 DEBUG NameMatchTransactionAttributeSource:94 - Adding transactional method [*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Exception]
2013-02-12 13:01:14 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'createUserTest' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'createUserTest' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 DEBUG JtaTransactionManager:365 - Creating new transaction with name [createUserTest]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 INFO TransactionalTestExecutionListener:279 - Began transaction (1): transaction manager [org.springframework.transaction.jta.JtaTransactionManager#2a3998f8]; rollback [false]
Created userusers.model.User#63aa9dc1[userId=0,first name=jonny,last name=doe,email=user5#domain.com,username=user5,password=1238,enabled=false]
2013-02-12 13:01:14 DEBUG JtaTransactionManager:470 - Participating in existing transaction
2013-02-12 13:01:14 DEBUG JtaTransactionManager:470 - Participating in existing transaction
Saved userusers.model.User#6b38579e[userId=0,first name=jonny,last name=doe,email=user5#domain.com,username=user5,password=1238,enabled=false]
2013-02-12 13:01:14 DEBUG JtaTransactionManager:752 - Initiating transaction commit
2013-02-12 13:01:14 WARN Preparer:69 - executing transaction with 0 enlisted resource
2013-02-12 13:01:14 INFO TransactionalTestExecutionListener:299 - Committed transaction after test execution for test context [TestContext#5a93c236 testClass = UserTest, testInstance = UserTest#1ab395af, testMethod = createUserTest#UserTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration#42821db testClass = UserTest, locations = '{classpath:tests-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.029 sec
2013-02-12 13:01:14 INFO GenericApplicationContext:1042 - Closing org.springframework.context.support.GenericApplicationContext#7d95609: startup date [Tue Feb 12 13:01:12 CET 2013]; root of context hierarchy
2013-02-12 13:01:14 INFO BitronixTransactionManager:320 - shutting down Bitronix Transaction Manager
2013-02-12 13:01:14 INFO LocalContainerEntityManagerFactoryBean:441 - Closing JPA EntityManagerFactory for persistence unit 'ruleEditor.persistence'
The logs show a testException=[null] but I have no idea why that would be.. I even tried to use the saveAndFlush() method provided by the JPARepository but doing so I get the error javax.persistence.TransactionRequiredException: no transaction is in progress
So if anyone has some idea of what I am doing wrong and can point me in the right direction I would very much appreciate the help.
After some careful reading of the logs, I found this warning:
WARN Ejb3Configuration:1309 - HHH000193: Overriding hibernate.transaction.factory_class is dangerous, this might break the EJB3 specification implementation
This was related to a property that I had set in the jpaPropertyMap bean in my tests-context.xml file:
<entry key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory" />
It turns out that once I removed this line in the configuration file I could see hibernate performing an "insert" action and the user was actually created in the database. Since I am new to spring and all the technologies I used, I must have copied the configuration from somewhere without really analysing it. So, I am now able to run junit4 tests in a project using spring data, hibernate and bitronix as transaction manager.
I am having trouble setting up a JUnit test that will rollback. I've read a few of these posts here on SO before deciding to write a question, as none seems to be the answer to my problem.
The second test case is failing because data is inserted and persisted in DB from the first test case.
Here's the JUnit test:
package com.company.group.spring.dao;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import com.company.group.spring.model.BusObj;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:NAME_OF_CONFIG_FILE.xml"})
#Transactional
#TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
#TestExecutionListeners({TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class})
public class PerPartsWCDAOTest {
#Autowired
#Qualifier("myBusObjDAO")
private BusObjDAO busObj_dao;
#Transactional
#Test
public void testInsertRollback() {
try {
BusObj busObj = new BusObj("some data...");
assertNotNull(busObj_dao);
assertNotNull(busObj);
busObj_dao.insert(perPartWC);
}
catch (NullPointerException e) {
e.printStackTrace();
}
}
#Transactional
#Rollback(false)
#Test
public void testInsert() {
BusObj busObj2 = new BusObj("some data...");
busObj_dao.insert(perPartWC2);
}
}
Here's the DAOImpl class (the class I'm trying to test):
package com.company.group.spring.dao;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.company.group.spring.model.BusObj;
#Repository("myBusObjDAO")
public class myBusObjDAOImpl implements myBusObjDAO {
private NamedParameterJdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
#Override
public void insert(BusObj someObj) {
String sql = "insert into TABLE (args...) " +
"VALUES (:named_params....)";
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(someObj);
jdbcTemplate.update(sql, paramSource);
}
}
Part of the relevant config:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#SERVER:PORT:DB"/>
<property name="username" value="USER"/>
<property name="password" value="XXXXXX"/>
</bean>
Edit: +debug print
2012-12-18 08:50:42,177 [main] DEBUG [org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction()] Creating new transaction with name [com.company.group.spring.service.MyClassService.create]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2012-12-18 08:50:42,342 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2012-12-18 08:50:42,344 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2012-12-18 08:50:42,361 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Exposing Hibernate transaction as JDBC transaction [org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler#128647a[valid=true]]
2012-12-18 08:50:42,390 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate.update()] Executing prepared SQL update
2012-12-18 08:50:42,391 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate.execute()] Executing prepared SQL statement [insert into TABLE (args...) VALUES (?, ?, SYSDATE,?, ?, SYSDATE, ?,?, ?)]
2012-12-18 08:50:42,391 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection()] Fetching JDBC Connection from DataSource
2012-12-18 08:50:42,657 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection()] Registering transaction synchronization for JDBC Connection
2012-12-18 08:50:42,671 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement()] SQL update affected 1 rows
2012-12-18 08:50:42,678 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection()] Returning JDBC Connection to DataSource
2012-12-18 08:50:42,688 [main] DEBUG [org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback()] Initiating transaction rollback
2012-12-18 08:50:42,688 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doRollback()] Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2012-12-18 08:50:42,693 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doCleanupAfterCompletion()] Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
Update:
I noticed in the log, it is trying to rollback a Hibernate transaction (but this is JDBC). Then noticed the class for "txManager" is currently org.springframework.orm.hibernate4.HibernateTransactionManager, so I experimented with org.springframework.jdbc.datasource.DataSourceTransactionManager and voila, rollback worked. So I think my problem is more specifically how I'm configuring to use Hibernate and JDBC... I still don't know what is wrong.
Your JUnit and DAO classes looks fine.
The problem might be that the connection is auto-committing before the transaction is rolled back. Can you change the dataSource bean to include a defaultAutoCommit property
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#SERVER:PORT:DB"/>
<property name="username" value="USER"/>
<property name="password" value="XXXXXX"/>
<property name="defaultAutoCommit" value="false" />
</bean>
If this is not working can you paste your console logs.
The solution to my problem was specifying the dataSource for the HibernateTransactionManager. Here's the documentation on HibernateTransactionManager.
So I guess it was a mistake not posting how I defined the txManager in the original post.
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
Changed to:
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
<property name="dataSource" ref="dataSource" />
</bean>
I also added <property name="dataSource"><ref local="dataSource"/></property> to the SessionFactory definition, but it doesn't seem necessary if I'm only worried about JdbcTemplate. Suspect it's needed though if HQL is intermixed in there.
Here's what the debug print looks like when it is working. The only difference I saw was this line Returning JDBC Connection to DataSource was absent before initiate rollback
2012-12-19 08:14:29,455 [main] DEBUG [org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction()] Creating new transaction with name [com.company.group.spring.service.MyClassService.create]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2012-12-19 08:14:29,512 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2012-12-19 08:14:29,514 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2012-12-19 08:14:29,539 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin()] Exposing Hibernate transaction as JDBC transaction [org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler#1289e48[valid=true]]
2012-12-19 08:14:29,558 [main] DEBUG [com.company.group.spring.dao.MyClassDAOImpl.insert()] Session Factory is: org.hibernate.internal.SessionFactoryImpl#1285252
2012-12-19 08:14:29,569 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate.update()] Executing prepared SQL update
2012-12-19 08:14:29,570 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate.execute()] Executing prepared SQL statement [insert into MY_CLASS (COL1, COL2, COL3, COL4, COL5, COL6, COL7, COL8, COL9) VALUES (?, ?, SYSDATE,?, ?, SYSDATE, ?,?, ?)]
2012-12-19 08:14:29,608 [main] DEBUG [org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement()] SQL update affected 1 rows
2012-12-19 08:14:29,611 [main] DEBUG [org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback()] Initiating transaction rollback
2012-12-19 08:14:29,612 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doRollback()] Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2012-12-19 08:14:29,680 [main] DEBUG [org.springframework.orm.hibernate4.HibernateTransactionManager.doCleanupAfterCompletion()] Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
I have a big problem with using JTA and Spring Data (1.9.2). My project works fine when I'm using JpaTransactionManager. I have also tried JdbcTemplate (no Spring Data) with JTA, that also works fine. I have tried OpenJPA (2.2.1) and Hibernate (4.2.8), both Jetty and Websphere Liberty server. They all fails using JTA and works fine using JPA Transaction Manager. The datasource is XA capable, and currently Derby Embedded. Springframework is v4.2.1
This is my Entity
#Entity
#Table(name = "SAKSSTATUS")
public class SaksStatus implements Serializable {
#Id
#Column(name = "ID")
private String id;
public SaksStatus() {};
public SaksStatus(String id) {
this.id = id;
}
}
My DAO
public interface SaksStatusDao extends JpaRepository<SaksStatus, String> {
}
My transaction
#Transactional
public void test() {
logger.info("deleting before testing ");
saksStatusDao.deleteAll();
logger.info("save...");
saksStatusDao.save(new SaksStatus("100"));
logger.info("save OK");
logger.info("result: {}", saksStatusDao.findAll());
logger.info("removing ...");
saksStatusDao.deleteAll();
logger.info("flush ...");
saksStatusDao.flush();
}
My application context :
<context:annotation-config/>
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/derbyEmbedded" />
<context:component-scan base-package="no.klp.saksstatus.dao" />
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:jta-transaction-manager />
<jpa:repositories base-package="no.klp.saksstatus.dao"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="no.klp.saksstatus.dao.entities" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"></bean>
<bean class="no.klp.saksstatus.services.SaksStatusService"/>
My persistence file
<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_2_0.xsd"
version="2.0">
<persistence-unit name="persistenceunit" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>no.klp.saksstatus.dao.entities.SaksStatus</class>
</persistence-unit>
</persistence>
My enhancer plugin in pom.xml
<plugin>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<version>2.3.0</version>
<configuration>
<includes>no/klp/saksstatus/dao/entities/**/*.class</includes>
<addDefaultConstructor>true</addDefaultConstructor>
<enforcePropertyRestrictions>true</enforcePropertyRestrictions>
<persistenceXmlFile>${basedir}/META-INF/persistence.xml</persistenceXmlFile>
</configuration>
<executions>
<execution>
<id>enhancer</id>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
</plugin>
So when executing my transaction I've got his log :
2016-02-02 13:34:52,391 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:367 - Creating new transaction with name [no.klp.saksstatus.services.SaksStatusService.test]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-02-02 13:34:52,423 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:30 - deleting before testing
2016-02-02 13:34:52,429 saksstatus-service DEBUG org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource:351 - Adding transactional method 'deleteAll' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-02-02 13:34:52,429 saksstatus-service DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory:250 - Returning cached instance of singleton bean 'transactionManager'
2016-02-02 13:34:52,430 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:476 - Participating in existing transaction
2016-02-02 13:34:52,456 saksstatus-service DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils:272 - Opening JPA EntityManager
2016-02-02 13:34:52,457 saksstatus-service DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils:290 - Registering transaction synchronization for JPA EntityManager
[err] 1409 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 26992656> executing prepstmnt 1533150303 SELECT t0.ID FROM SAKSSTATUS t0
[err] 1410 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 26992656> [0 ms] spent
2016-02-02 13:34:52,674 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:32 - save...
2016-02-02 13:34:52,675 saksstatus-service DEBUG org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource:351 - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-02-02 13:34:52,675 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:476 - Participating in existing transaction
[err] 1525 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 1984659730> executing prepstmnt 715039869 SELECT t0.ID FROM SAKSSTATUS t0 WHERE t0.ID = ? [params=?]
[err] 1526 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 1984659730> [0 ms] spent
2016-02-02 13:34:52,778 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:34 - save OK
2016-02-02 13:34:52,779 saksstatus-service DEBUG org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource:351 - Adding transactional method 'findAll' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
2016-02-02 13:34:52,779 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:476 - Participating in existing transaction
[err] 1539 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 977455988> executing prepstmnt 1441490501 SELECT t0.ID FROM SAKSSTATUS t0
[err] 1539 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 977455988> [0 ms] spent
2016-02-02 13:34:52,781 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:35 - result: []
2016-02-02 13:34:52,781 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:36 - removing ...
2016-02-02 13:34:52,781 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:476 - Participating in existing transaction
[err] 1541 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 1721406628> executing prepstmnt 174725407 SELECT t0.ID FROM SAKSSTATUS t0
[err] 1541 default TRACE [Default Executor-thread-2] openjpa.jdbc.SQL - <t 925948525, conn 1721406628> [0 ms] spent
2016-02-02 13:34:52,783 saksstatus-service INFO no.klp.saksstatus.services.SaksStatusService:38 - flush ...
2016-02-02 13:34:52,783 saksstatus-service DEBUG org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource:351 - Adding transactional method 'flush' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-02-02 13:34:52,784 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:476 - Participating in existing transaction
2016-02-02 13:34:52,785 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:858 - Participating transaction failed - marking existing transaction as rollback-only
2016-02-02 13:34:52,785 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:1072 - Setting JTA transaction rollback-only
2016-02-02 13:34:52,786 saksstatus-service DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory:250 - Returning cached instance of singleton bean 'entityManagerFactory'
2016-02-02 13:34:52,790 saksstatus-service DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils:435 - Closing JPA EntityManager
2016-02-02 13:34:52,802 saksstatus-service DEBUG org.springframework.transaction.jta.JtaTransactionManager:851 - Initiating transaction rollback
[ERROR ] SRVE0777E: Exception thrown by application class 'org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible:413'
org.springframework.dao.InvalidDataAccessApiUsageException: Can only perform operation while a transaction is active.; nested exception is <openjpa-2.2.1-r422266:1396819 nonfatal user error> org.apache.openjpa.persistence.TransactionRequiredException: Can only perform operation while a transaction is active.
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:413)
at org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:122)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy41.flush(Unknown Source)
at no.klp.saksstatus.services.SaksStatusService.test(SaksStatusService.java:39)
at no.klp.saksstatus.services.SaksStatusService$$FastClassBySpringCGLIB$$fcbe12b7.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at no.klp.saksstatus.services.SaksStatusService$$EnhancerBySpringCGLIB$$db202906.test(<generated>)
at no.klp.saksstatus.TestServlet.doGet(TestServlet.java:49)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:575)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1287)
at [internal classes]
Caused by: <openjpa-2.2.1-r422266:1396819 nonfatal user error> org.apache.openjpa.persistence.TransactionRequiredException: Can only perform operation while a transaction is active.
at org.apache.openjpa.kernel.BrokerImpl.assertActiveTransaction(BrokerImpl.java:4694)
at org.apache.openjpa.kernel.DelegatingBroker.assertActiveTransaction(DelegatingBroker.java:1386)
at org.apache.openjpa.persistence.EntityManagerImpl.flush(EntityManagerImpl.java:662)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344)
at com.sun.proxy.$Proxy38.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293)
at com.sun.proxy.$Proxy38.flush(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:486)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 23 more
One difference from using JPA transaction manager, is the only SELECT statments are logged. The INSERT statement is not executed (when using JPA Transaction Manager, they where all logged).
The save entry (saksStatusDao.save(new SaksStatus("100"));) should create an INSERT. And to the end a DELETE should be executed.
Everything seems very fine, but the last flush() is starting the rollback, please give me some advice to solve this. Have been struggeling with this for a month now ....
Using JPA transaction manager is not a option as I want to use JTA and allowCustomIsolationLevels to utilize UNCOMITTED_READ.
/Bjørn-Willy Arntzen, Oslo, Norway