Deadlock in a Spring+Hibernate+DB2+JTA+XA application - java

Exception from application log:
12:04:18,503 INFO ExceptionResolver:30 - [ org.springframework.dao.DeadlockLoserDataAccessException ] Hibernate flushing: could not update: [sero.chase.integration.Beans.Bean#1000]; SQL [update SCHM.v***240u_bean set prop1=?, prop2=?, prop3=?, prop4=?, prop5=?, prop6=?, prop7=?, prop8=?, prop9=?, prop10=?, prop11=?, prop12=?, prop13=?, prop14=?, prop15=?, prop16=?, prop17=?, prop18=?, prop19=?, prop20=?, prop21=?, where bean_id=?]; UNSUCCESSFUL EXECUTION CAUSED BY DEADLOCK OR TIMEOUT. REASON CODE 00C90088, TYPE OF RESOURCE 00000302, AND RESOURCE NAME SCHM.SAKT240 .X'000017'. SQLCODE=-913, SQLSTATE=57033, DRIVER=3.53.70; nested exception is com.ibm.db2.jcc.b.SqlException: UNSUCCESSFUL EXECUTION CAUSED BY DEADLOCK OR TIMEOUT. REASON CODE 00C90088, TYPE OF RESOURCE 00000302, AND RESOURCE NAME SCHM.SAKT240 .X'000017'. SQLCODE=-913, SQLSTATE=57033, DRIVER=3.53.70org.springframework.dao.DeadlockLoserDataAccessException: Hibernate flushing: could not update: [sero.chase.integration.Beans.Bean#1000]; SQL [update SCHM.v***240u_bean set prop1=?, prop2=?, prop3=?, prop4=?, prop5=?, prop6=?, prop7=?, prop8=?, prop9=?, prop10=?, prop11=?, prop12=?, prop13=?, prop14=?, prop15=?, prop16=?, prop17=?, prop18=?, prop19=?, prop20=?, prop21=?, where bean_id=?]; UNSUCCESSFUL EXECUTION CAUSED BY DEADLOCK OR TIMEOUT. REASON CODE 00C90088, TYPE OF RESOURCE 00000302, AND RESOURCE NAME SCHM.SAKT240 .X'000017'. SQLCODE=-913, SQLSTATE=57033, DRIVER=3.53.70; nested exception is com.ibm.db2.jcc.b.SqlException: UNSUCCESSFUL EXECUTION CAUSED BY DEADLOCK OR TIMEOUT. REASON CODE 00C90088, TYPE OF RESOURCE 00000302, AND RESOURCE NAME MWIAKT1 .SAKT240 .X'000017'. SQLCODE=-913, SQLSTATE=57033, DRIVER=3.53.70
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:265)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertJdbcAccessException(HibernateTransactionManager.java:805)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:791)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:664)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy54.save(Unknown Source)
at sero.chase.integration.DaoImpl.ExampleDaoImpl.save(ExampleDaoImpl.java:151)
at sero.chase.business.BOImpl.ExampleBOImpl.save(ExampleBOImpl.java:191)
at sero.chase.ServicesImpl.ExampleServiceImpl.submitAnswer(ExampleServiceImpl.java:183)
at sero.chase.business.BusDelegatesImpl.ExampleBusDelegateImpl.gradeAnswer(ExampleBusDelegateImpl.java:578)
at sero.chase.presentation.Controller.ExampleController.gradeAnswer(ExampleController.java:326)
at sero.chase.presentation.Controller.ExampleController.SubmitAnswer(ExampleController.java:422)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:471)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:408)
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:763)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1152)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1087)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:118)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:840)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:683)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:589)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:534)
at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:90)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:751)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1478)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:126)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:102)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:136)
at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:196)
at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:751)
at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
Caused by: com.ibm.db2.jcc.b.SqlException: UNSUCCESSFUL EXECUTION CAUSED BY DEADLOCK OR TIMEOUT. REASON CODE 00C90088, TYPE OF RESOURCE 00000302, AND RESOURCE NAME MWIAKT1 .SAKT240 .X'000017'. SQLCODE=-913, SQLSTATE=57033, DRIVER=3.53.70
at com.ibm.db2.jcc.b.bd.a(bd.java:679)
at com.ibm.db2.jcc.b.bd.a(bd.java:60)
at com.ibm.db2.jcc.b.bd.a(bd.java:127)
at com.ibm.db2.jcc.b.fm.b(fm.java:2132)
at com.ibm.db2.jcc.b.fm.c(fm.java:2115)
at com.ibm.db2.jcc.t4.db.k(db.java:353)
at com.ibm.db2.jcc.t4.db.a(db.java:59)
at com.ibm.db2.jcc.t4.t.a(t.java:50)
at com.ibm.db2.jcc.t4.tb.b(tb.java:200)
at com.ibm.db2.jcc.b.gm.Zb(gm.java:2445)
at com.ibm.db2.jcc.b.gm.e(gm.java:3287)
at com.ibm.db2.jcc.b.gm.Rb(gm.java:612)
at com.ibm.db2.jcc.b.gm.executeUpdate(gm.java:595)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.executeUpdate(WSJdbcPreparedStatement.java:768)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:23)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2399)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2303)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2603)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:92)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JTATransaction.commit(JTATransaction.java:135)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 50 more
Spring configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:jee="http://www.springframework.org/schema/jee"
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
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<jee:jndi-lookup id="queueConFac" resource-ref="true" jndi-name="jms/queueConFac" />
<jee:jndi-lookup id="receiveQ" resource-ref="true" jndi-name="jms/receiveQ" />
<jee:jndi-lookup id="sendQ" resource-ref="true" jndi-name="jms/sendQ" />
<jee:jndi-lookup id="XA" resource-ref="true" jndi-name="jdbc/XA" />
<jee:jndi-lookup id="nonXA" resource-ref="true" jndi-name="jdbc/nonXA" />
<bean id="jmsTxManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
<bean id="jmsDestResolver" class=" org.springframework.jms.support.destination.JndiDestinationResolver"/>
<bean id="exampleListener" class="sero.chase.integration.JMS.Services.JMSReceiver">
<property name="exampleAppBusDelegate" ref="exampleAppBusDelegate" />
</bean>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
<property name="connectionFactory" ref="queueConFac" />
<property name="destination" ref="receiveQ" />
<property name="messageListener" ref="exampleListener" />
<property name="transactionManager" ref="jmsTxManager" />
<property name="taskExecutor" ref="jmsTaskExecutor" />
</bean>
<bean id="jmsTaskExecutor"
class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default" />
</bean>
<bean id="jmsSender" class="sero.chase.integration.JMS.Services.JMSSender">
<property name="connectionFactory" ref="queueConFac" />
<property name="queue" ref="sendQ" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="WEB-INF/resources/langSpecificText"/>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-def.xml</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/nonXA" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="nonXA" />
<property name="configLocation" value="classpath:/hibernate.cfg.nonXA.xml" />
<property name="entityInterceptor">
<bean class="sero.chase.integration.Hibernate.DB2Interceptor"/>
</property>
</bean>
<bean id="session.XA.Factory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="XA" />
<property name="configLocation" value="classpath:/hibernate.cfg.XA.xml" />
<property name="entityInterceptor">
<bean class="sero.chase.integration.Hibernate.DB2Interceptor"/>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transaction.XA.Manager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="session.XA.Factory" />
</bean>
<bean id="transactionAttributeSource"
class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- App Bean Definitions (Two dao configurations excluding several other bean configurations are displayed below) -->
<bean id="exampleDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource" ref="transactionAttributeSource" />
<property name="target">
<bean class="sero.chase.integration.PersistenceImpl.ExamplePersistenceImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</property>
</bean>
<bean id="exampleXADao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true">
<property name="transactionManager" ref="transaction.XA.Manager" />
<property name="transactionAttributeSource" ref="transactionAttributeSource" />
<property name="target">
<bean class="sero.chase.integration.PersistenceImpl.ExamplePersistenceImpl">
<property name="sessionFactory" ref="session.XA.Factory" />
</bean>
</property>
</bean>
</beans>
Hibernate Non XA configuration:
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
sero.chase.integration.Hibernate.DB2390Dialect
</property>
<property name="hibernate.default_schema">SCHM</property>
<property name="query.substitutions">yes 'Y', no 'N'</property>
<property name="jdbc.use_streams_for_binary">true</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="jta.UserTransaction">java:comp/UserTransaction</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!--===============-->
<!-- mapping files -->
<!--===============-->
<mapping resource="sero/chase/integration/hbm/Example.hbm.xml" />
</session-factory>
</hibernate-configuration>
Hibernate XA configuration:
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
sero.chase.integration.Hibernate.DB2390Dialect
</property>
<property name="hibernate.default_schema">SCHMA</property>
<property name="query.substitutions">yes 'Y', no 'N'</property>
<property name="jdbc.use_streams_for_binary">true</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory </property>
<property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!--===============-->
<!-- mapping files -->
<!--===============-->
<mapping resource="sero/chase/integration/hbm/Example.hbm.xml" />
</session-factory>
</hibernate-configuration>
Code snippet from my service implementation class where most of the business logic happens:
public void someDeadlockCausingServiceMethod() {
//The read1() method below is going to executing a select hql statement on Table A once the call goes all the way down to the Dao layer.
List<SomeBeanInBusinessLayer> beanList = exampleBO.read1();
//Do some processing with the values obtained from the read1() method
...
//
//saveOrUpdate() method below is going to execute an update hql statement on Table A once the call goes all the way down to the Dao layer
//where the values from someBeanInBusinessLayer is going to be copied into someBeanInDaoLayer before saving.
exampleBO.saveOrUpdate(someBeanInBusinessLayer)
//The read2() method below is going to execute a select hql statement that contains two inner selects (on Table B, C and D) once the call goes all
//the way down to the Dao layer.
List<SomeBeanInBusinessLayer2> beanList2 = exampleBO.read2();
//Do some processing with the values obtained from the read2() method inside a for loop
for(SomeBeanInBusinessLayer2 s: beanList2) {
//Read values from table E
List<SomeBeanInBusinessLayer3> beanList3 = exampleBO.read3(s.getProp2());
SomeBeanInBusinessLayer2 someBeanInBusinessLayer2 = new SomeBeanInBusinessLayer2();
someBeanInBusinessLayer2.setProp1(s.getProp2());
someBeanInBusinessLayer2.setProp1(someBeanInBusinessLayer3.getProp2());
//... more processing...
//Below method will execute an insert hql on Table F
exampleBO.saveOrUpdate(someBeanInBusinessLayer2);
SomeBeanInBusinessLayer3 someBeanInBusinessLayer3 = new SomeBeanInBusinessLayer3();
someBeanInBusinessLayer3.setProp1(s.getProp5());
//... more processing...
//Below method will execute an insert hql on Table G
exampleBO.saveOrUpdate(someBeanInBusinessLayer3);
}
}
public void anotherDeadlockCausingServiceMethod() {
//The read1() method below is going to executing a select hql statement on Table A once the call goes all the way down to the Dao layer.
List<SomeBeanInBusinessLayer> beanList = exampleBO.read1();
//The read2() method below is going to executing a select hql statement on Table F once the call goes all the way down to the Dao layer.
List<SomeBeanInBusinessLayer2> beanList2 = exampleBO.read2();
//The read1() method below is going to executing a select hql statement on Table G once the call goes all the way down to the Dao layer.
List<SomeBeanInBusinessLayer3> beanList3 = exampleBO.read3();
//Do some processing with the values obtained...
//Do an update on Table A
exampleBO.saveOrUpdate(someBeanInBusinessLayer1)
//Do an update on Table F
exampleBO.saveOrUpdate(someBeanInBusinessLayer2)
}
Code snippet from my Dao layer1:
public void load(BeanDTO beanDTO) {
Object param1 = beanDTO.getBeanList().getProp1();
Object param2 = beanDTO.getBeanList().getProp2();
List<SomeBeanInDaoLayer> beanList = null;
Object[] params = {param1, param2};
UserTransaction ut = null;
try {
Context context = new InitialContext();
ut = (UserTransaction) context.lookup(Constant.USR_TRANSACTION);
ut.begin();
beanList = beanDao2.load(params);
ut.commit();
}
catch(Exception e) {
try {
ut.rollback();
}
catch(Exception e1) {
if(logger.isDebugEnabled()) {
logger.debug("DB Exception", e1);
}
}
int error = ExceptionResolver.resolve(e);
if(logger.isDebugEnabled()) {
logger.debug("DB Exception", e);
}
beanDTO.setErrorCode(error);
}
beanDTO.setBeanList(beanList);
}
public void save(BeanDTO beanDTO) {
List<SomeBeanInDaoLayer> beanList = beanDTO.getBeanList();
for(SomeBeanInDaoLayer bean: beanList) {
try {
Context context = new InitialContext();
ut = (UserTransaction) context.lookup(Constant.USR_TRANSACTION);
ut.begin();
beanDao2.save(bean);
ut.commit();
}
catch(Exception e) {
try {
ut.rollback();
}
catch(Exception e1) {
if(logger.isDebugEnabled()) {
logger.debug("DB Exception", e1);
}
}
int err = ExceptionResolver.resolve(e);
if(logger.isInfoEnabled()) {
logger.info("DB Exception", e);
}
beanDTO.setErrorCode(err);
}
}
}
Code snipper from my Dao Layer2:
public List<Bean> load(Object[] params) {
String hql = "from Bean where beanProp1 = ? and beanProp2 = ?";
return (List<Bean>) getHibernateTemplate().find(hql, params);
}
public void save(Bean bean) {
getHibernateTemplate().saveOrUpdate(bean);
}
This application is a testing system where in users can take test
concurrently.
Initially the transaction demarcation was not at my Dao layer but
say at my Service Implementation class (actually all the way at my
controller class) where several reads and updates were tied up into
one transaction within a begin-commit block. Since I was seeing
several deadlocks I moved the demarcation to Dao layer so that
there's only one hql statement between my begin-commit block to see
if it prevents the deadlock but have had no luck.
Tried setting properties like hibernate.connection.autocommit to
true, hibernate.transaction.flush_before_completion to true and
hibehibernate.transaction.auto_close_session to true but had no
luck.
Never a row read by one user is updated by another user. Each user
reads and updates different rows even though they access the same
DB2 tables. Only at the time of running the process for building a
set of questions for a test, two users would read the same rows if
they were taking the same type of test. It is very similar to the
someDeadlockCausingMethod described above where the test questions
are prepared from a set of tables that contain questions and
answers. From iterating through this result set inside a for-loop,
new rows are inserted into another table to save the details of each
question that will appear on an user's test. This step is necessary
in the application because even though two users take the same test,
a set of random questions are taken from the pool of all questions
for each user.
Now that the test is prepared for user taking the test, the next
logical step in the application is to read the rows from a table
that contain just the details of the questions pertaining to the
user taking the test. So a concurrent user would read the same table
during this process but never the same row. The user is presented
with one question at a time. Once the user answers a question, the
row that was read to get the question pertaining to just this user
will be updated with the answer choice. Again never the same rows
are getting updated for two concurrent users. This method is
analogous to anotherDeadlockCausingMethod() described above.
I hope you got an idea of what the application does. The fact that
never the same rows are read or updated by concurrent users
surprised me on how a resource can get locked. Then I figured out
that page-locking was in place for the tables getting updated. So I
went and asked the DBA if he could change it to row-locking on the
tables that were getting updated. He is concerned about the
performance overhead on DB2 to implement row-locking and is worried
if it may affect other applications using DB2. So he doesn't want to
do it unless I can't find any other solution.
Please forget the XA/JMS portion. Assume that portion is commented
out for time being. For the most part of the application non-XA
datasource is used where I see the deadlocks.
Can someone please tell me how do I go about resolving the deadlock? I want to understand what went wrong in the design. Any help is greatly appreciated in advance

As you are performing several insert,update,delete i would like to suggest you adding
<property name="hibernate.connection.autocommit" value="false"/>
And once all you queries executed successfully then do the commit manually connection.commit();
Maybe this could help you.

Finally I managed a clean run without a deadlock doing the following:
hibernate.connection.isolation = 2
Added 'for update of with cs' at the end of my select statements.
I was using the saveOrUpdate() hibernate method for both update and insert. Now instead I use save() for insert and update() for update.
One thing I didn't understand was why using 'with ur' at the end of my select statements didn't resolve the deadlocks in comparison to the 'with cs' I'm using now. Wondering has the isolation level of the database, which is 'rs' got to do anything with it?

Related

Spring #Transactional - Rollback is not happening

I am new to spring and working on a sample program using Spring jdbc. this is to check how spring #Trsactional working and rolling back the changes to the Db if there is an exception.
But I am not able to achieve this. Through I am raising an exception in one of the DB update, still it's inserting the data to DB and not rolling back for that batch. For ex. after inserting 5000 I am raising an exception, so ideally it should rollback all the changes(for the current batch) to all the tables and total rows in Db should be 4000.
I know somewhere I am making mistake but not able to figure it out. Not sure if this is a correct approach.
I tried all possible ways available in the Internet, but still no luck. please help me to resolve this issue.
Here is my sample application https://github.com/rajarshp/JavaSample
Code Snippet
#Transactional(rollbackFor={Exception.class})
public void executeDB(int count) throws Exception
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
//BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
//newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
//newbal.addBalance(); // insert data to db
newacc.getAccList().clear();
newusr.getUserList().clear();
//newbal.getBalanceList().clear();
if(count == 5000)
{
Thread.sleep(1000);
throw new Exception("Rollback");
}
count += 1000;
//throw new Exception();
}
XML:
<context:component-scan base-package="com.example"></context:component-scan>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<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="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="root" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="startit" class="com.example.springtransaction.GlobalClass">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
Create bean for all Db operation classes like createUser or Cretae account in the xml file. remove the initialization on these classes from db operation class and use setter method to inject it from the xml. Post that call your db operation method. It should work.
<bean id="newaccount" class="com.example.springtransaction.CreateAccount">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="newuser" class="com.example.springtransaction.CreateUser">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="newaccount" ref="newacc"></property>
<property name="newuser" ref="newusr"></property>
</bean>

Java, MyBatis 3.4, huge batch inserts are not actually stored to DB at once

Task: i need to do at least 10k records inserts as a one batch.
In reality: During the commit/flush calls I can see by using "SELECT * FROM TABLE" that records are inserted by totally random numbers.
Like it can insert 1k, then 500, then another 1.5k, then again 500. Some really weird stuff is going on with this batching.
I have really basic setup, as:
<bean id="armDataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="${database.url}"/>
<property name="user" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="maxPoolSize" value="30"/>
<property name="idleConnectionTestPeriod" value="300"/>
<property name="maxIdleTime" value="300"/>
<property name="preferredTestQuery" value="SELECT 1"/>
<property name="testConnectionOnCheckin" value="true"/>
</bean>
<tx:annotation-driven />
<bean id="armTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="armDataSource"/>
</bean>
<bean id="armSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="armDataSource"/>
<property name="typeAliasesPackage" value="com.database.arm.model"/>
</bean>
<bean id="armSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="armSqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
Then i have the following code:
SqlSession sqlSession = armSqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
HeaderMapper headerMapper = sqlSession.getMapper(HeaderMapper.class);
List<List<HeaderDAO>> partitions = Lists.partition(HeaderDAOS, 10000);
partitions.forEach(partition -> {
partition.forEach(headerDAO -> {
headerDAO.setFileId(fileId);
headerMapper.insert(headerDAO);
});
sqlSession.flushStatements();
});
sqlSession.commit();
sqlSession.close();
It works just fine with small batch inserts of 100 or less records. But once i go higher, it looks like mybatis doing more then one batch.
Maybe there is some problem with the setup of sqlSessionFactory or template or dataSource? I've tried so many options but neither helped.
Any input is very appreciated.
So I have debugged all the stack till mysql driver and have found the reason.
By default, MySQL driver actually performs batch inserts one by one in one transaction.
To enable one big batch insert, you need to let mysqldriver know it, by using property:
rewriteBatchedStatements=true
This can be bypassed in the connectionUrl for the datasource, or specified via properties of the datasource:
<property name="properties">
<props>
<prop key="rewriteBatchedStatements">true</prop>
</props>
</property>
P.S.: You need to use it only in case of MyBatis, because for instance, Hibernate will do it automatically.

Hibernate's multi tenancy using separate schema

The problem
I have an application build on Spring 4, Hibernate 5 and Spring Data JPA 1.7. I use PostgresSQL as database. I'd like to use Hibernate's support for multi tenancy, but have problem with correctly implementing MultiTenantConnectionProvider. I'd like to use SCHEMA stratagey for separating tenants and I'd prefer to use single DataSource.
My current implementation of MultiTenantConnectionProvider's method getConnection() looks like this:
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
I take the connection from my DataSource, which I inject by implementing interface ServiceRegistryAwareService, but I'm not sure that this is a right way.
Method gets called when it should with correct tenantIdentifier (comming from my implementation of CurrentTenantIdentifierResolver), statement is executed, but in practise, it is useless. Problem is, that queries generated by Hibernate contain fully qualified names of tables including default schema. Can I tell hibernate to omit default schema from queries? Or should I use completely different approach?
My configuration
I don't wanna clutter my question with too much configuration, I'll paste here what I believe is relevant. If I missed something important, please let me know.
This is part of my application context xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="pu" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQL9Dialect" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
Side question
Is there any difference between hibernate.multiTenancy values SCHEMA and DATABASE, from Hibernate's point of view? I understand the conceptual difference between these two, but from looking at the source code, it seems to me like these two are completely interchangeable and all the logic is hidden in the implementation of MultiTenantConnectionProvider. Am I missing something?

Spring #Transaction not working with with LocalContainerEntityManagerFactoryBean

I'm developing an application that receives various XML files containing different 'Individual' objects that I store in a database.
The goal is that all the files are read and treated. If a file is processed correctly it moves to the "processed" folder. If an exception is thrown it moves to an error folder.
The desired behaviour is that if an error occurs in one of the files, everything is rollbacked , nothing is saved in the database and all the files are copied to the error folder (also the already processed ones)
The copying of the folders probably can't be done using transactions so I do them manually...
The structure of my project is the following:
Technologies:
Hibernate : 3.5.0-Final
Spring : 3.1.1.RELEASE
Server : Tomcat 7
Database : SQL Server
I start from the idea that the best location to put the transaction is the service. I don't add the propagation property, since I want the default Property.REQUIRED behaviour:
#Transactional(rollbackFor = Exception.class)
private Feedback readIndividuals(File fileLocation) throws Exception {
System.out.println("Start reading individuals");
//Set the status of all database entries to DELETED
individualEntityService.setAllStatussesToDeleted();
}
final File individualsProcessedFolder = new File(individualsProcessedFolderLocation);
for (final File fileEntry : fileLocation.listFiles()) {
if (fileEntry.isDirectory()) {
readIndividuals(fileEntry, feedback);
} else {
individualReader.read(fileEntry.getAbsolutePath());
....
Here I start the transaction. The individualReader is a service that performs the actual reading of the file and the writing to the DB.
EDIT
Here the code of the IndividualReader where I call the add method in the EntityService:
#Override
#Transactional
public void read(String fileLocation) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(CDM.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
XMLInputFactory factory = XMLInputFactory.newInstance();
FileInputStream fileInputStream = new FileInputStream(fileLocation);
XMLStreamReader xmlr = factory.createXMLStreamReader(fileInputStream, ENCODING);
try {
xmlr.nextTag();
xmlr.require(XMLStreamConstants.START_ELEMENT, null, "CDM");
xmlr.nextTag();
while (xmlr.getEventType() == XMLStreamConstants.START_ELEMENT) {
JAXBElement<CDM.Individual> jaxbIndividual = unmarshaller.unmarshal(xmlr,
CDM.Individual.class);
CDM.Individual individual = jaxbIndividual.getValue();
Individual individualDO = individualBuilder.build(individual);
Set<Diploma> diplomas = diplomaBuilder.build(individual.getDiplomas(), individualDO);
Set<HealthCareProfessional> healthCareProfessionals = healthCareProfessionalBuilder.build(individual.getHCProfessionals());
individualDO.addHealthCareProfessionals(healthCareProfessionals);
individualDO.addDiplomas(diplomas);
LOG.debug("Adding individual with SSIN [" + individualDO.getSsin() + "] into DB");
Individual retrievedIndividual = individualEntityService.read(individualDO.getSsin());
if (retrievedIndividual != null) {
individualEntityService.remove(retrievedIndividual);
individualDO.setStatus(EntryStatus.UPDATED);
}
individualEntityService.add(individualDO);
LOG.debug("Individual with SSIN [" + individualDO.getSsin() + "] successfully added to DB");
LOG.debug(getIndividualXMLAsString(individualDO));
if (xmlr.getEventType() == XMLStreamConstants.CHARACTERS) {
xmlr.next();
}
}
} finally {
xmlr.close();
fileInputStream.close();
}
}
The lower level is the EntityService:
#Override
#Transactional
public void add(Individual individual) {
individualDao.addIndividual(individual);
}
This class doesn't do anything more than calling the DAO, I annotated it with the #Transactional annotation. Since the default is Propagation.REQUIRED it won't start a new physical transaction, but it will join the transaction of the service.
Finally we have the DAO:
#Transactional
public void addIndividual(Individual individual) {
em.persist(individual);
}
I also annotate this method with Transactional, with the same reason as above.
The Entity manager is autowired in the DAO using Spring:
#PersistenceContext
private EntityManager em;
The Entity Manager is defined in the applicationContext as follows:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
Now everything compiles and deploys fine and works even as expected. But when I make one of the XML files corrupt all the files before the corrupt file end up in the DB and the transaction is not rollbacked.
I guess I must be missing something and probably my mistake is in the wrong usage of the combination #Transaction and the Spring EntityManager. I never use the explicit em.flush() to push the data to the DB. Maybe the em.persist is wrong and stores the data to the database and I can't recover from it...
Anyone has an idea of what I'm doing wrong? Help would be highly appreciated.
EDIT Hereby the complete context:
<?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:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:component-scan base-package="be.healthconnect.pwg" />
<task:annotation-driven />
<tx:annotation-driven />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:/be/healthconnect/pwg/core/properties/pwg.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${datasource.driver.class.name}" />
<property name="url" value="${datasource.url}" />
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.password}" />
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jpaVendorAdaptor"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
</beans>
The mistake I made was the following: #Transactional annotation had no effect because it annotated a private method. The proxy generator was ignoring it.
I found the solution in the Spring Manual chapter 10.5.6:
Method visibility and #Transactional
When using proxies, you should apply the #Transactional annotation only to methods with public visibility.
If you do annotate protected, private or package-visible methods with the #Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

Spring global transaction committed after getting an element

I am using Spring and Hibernate with Jta Transactions, I have 2 databases, and I have a problem in a transactional method.
In this method I insert a lot of objects but I throws an exception to rollback the insertions, here the code works as I expected because the objects dont appear into the database.
But if I add a line in the method that get the objects of the same table, the objects are committed into the database.
I think that when I make a SELECT the objects are auto-committed, because the exception its thrown again and the objects persists into the database.
My xml and code:
dao.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:configuracion_dao.properties" />
</bean>
<bean name="productosDAO" class="practica1.hibernate.HibernateProductosDAOImpl"
parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory" />
</bean>
<bean name="tercerosDAO" class="${tercerosDAO.classname}" parent="abstractPracticaBean">
<property name="dataSource" ref="dataSourceDatos" />
</bean>
<bean name="auditoriaDAO" class="practica1.hibernate.HibernateAuditoriaDAOImpl" parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory2" />
</bean>
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceDatos" />
<property name="mappingResources">
<list>
<value>hibernate-mappings.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="hibernateSessionFactory2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceAuditoria" />
<property name="mappingResources">
<list>
<value>hibernate-mappings-auditoria.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean name="dataSourceDatos" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/datos.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceDatos"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_derby.sql" />
<jdbc:script location="classpath:practica1/sql/datos.sql" />
</jdbc:initialize-database>
<bean name="dataSourceAuditoria" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/auditoria.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceAuditoria"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_auditoria_derby.sql" />
</jdbc:initialize-database>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" value="#{jotm.transactionManager}" />
<property name="userTransaction" value="#{jotm.userTransaction}" />
</bean>
<bean id="jotm" class="org.objectweb.jotm.Jotm" destroy-method="stop">
<constructor-arg value="true" />
<constructor-arg value="false" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
bo.xml
<bean name="tercerosBO" class="practica1.impl.TercerosBOImpl"
parent="abstractPracticaBean" autowire="constructor">
</bean>
<bean name="productosBO" class="practica1.impl.ProductosBOImpl"
parent="abstractPracticaBean">
<property name="productosDAO" ref="productosDAO" />
<property name="auditoriaDAO" ref="auditoriaDAO" />
</bean>
aplicacion.xml
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="mensajes" />
</bean>
<bean id="abstractPracticaBean" class="practica1.impl.AbstractPracticaBean" abstract="true">
<property name="messageSource" ref="messageSource"></property>
</bean>
<import resource="bo.xml" />
<import resource="dao.xml" />
Transactional method:
#Transactional
#Override
public void actualizaPrecio(double porcentaje) {
internalActualizaPrecio(porcentaje);
}
private void internalActualizaPrecio(double porcentaje) {
auditoriaDAO.insertAuditoria(getMessageSource().getMessage(
"mensaje.actualizar_productos", new Object[] { porcentaje },
null));
int i = 0;
auditoriaDAO.getAuditorias(); // Without this line its works like I expected
List<Producto> productos = productosDAO.getProductos();
for (Producto producto : productos) {
i++;
if (i > 3)
throw new UnsupportedOperationException(
"Error para que veamos las transacciones");
producto.setPrecio(producto.getPrecio().multiply(
new BigDecimal(porcentaje).divide(new BigDecimal(100))));
productosDAO.updateProducto(producto);
}
}
I realised that if I use auditoriaDAO.getAuditorias() the rollback only affects to Producto but if I use productoDAO.getProductos() the rollback only affects to Auditoria...
You may be mixing up flush and commit here: a SELECT statement usually flushes all previous SQL statements, in order to fetch up-to-date data (regarding the previous changes you made in the tx). It may be possible that before such a SELECT statement is done (the following DAO calls are made to the 2nd sessionFactory if I'm not mistaken), the exception exits the method without a flush. Hence no modification in database.
So the question is: are you sure you're rollbacking the tx effectively? I see you've annotated a private method: the proxy-based mechanism of Spring AOP don't handle that! You must annotate a public method and call it from outside the annotated method's class, due to this very proxy-based mechanism. See the "Method visibility and #Transactional" block in the documentation.
Another lead: you have 2 sessionFactories, so I assume you're using XA transactions/datasources: are you sure this part of the conf is OK?
Please check auditoriaDAO and productosDAO and search for other transactional annotation. I think a new transaction is created somewhere and the UnsupportedException rollbacks only the last transaction, and the parent transaction is committed. Hope I helped!
I have found two example. Please check it.
JOTM transactions in spring and hibernate
Access Multiple Database Using Spring 3, Hibernate 3 and Atomikos

Categories

Resources