We are using hibernateTemplate for our dao's within my current project. I have a situation where I want to have a transaction that rolls back if either operation within the transaction fails (not rocket science, right?).. so basically, here is the method:
// Reserve a deal and add the 'pending order' all in one transaction
#Transactional(propagation = Propagation.REQUIRES_NEW)
private MyUserOrder reserveQuantity(MyUserOrder userOrder, Date updatedOn) throws MyAPIException{
userOrder.setOrderStatus(OrderStatus.PENDING);
int orderReserved = orderDao.reserveQuantity(userOrder);
//for some reason hibernate is flushing on the above line???
if (dealsReserved < 1)
throw new MyAPIException(ExceptionCode.INSUFFICIENT_QUANTITY);
userOrder = userOrderDao.save(userOrder);
//hibernate flushes again!
return userOrder;
}
So, basically we reserve X orders from the databases order availability table... and then save the pending order to the database. I'd like both statements to execute in the same transaction, so if one fails they both roll back. Unfortunately, it looks like the statement for .reserveQuantity is being committed immediately, even though the whole method should run within it's own transaction.
Here is the log..
13:09:28.007 [5253145#qtp-25442933-1] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13291529680
13:09:28.008 [5253145#qtp-25442933-1] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Registering Spring transaction synchronization for new Hibernate Session
13:09:28.009 [5253145#qtp-25442933-1] DEBUG o.s.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
13:09:28.011 [5253145#qtp-25442933-1] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
13:09:28.011 [5253145#qtp-25442933-1] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
13:09:28.012 [5253145#qtp-25442933-1] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:db2://172.26.10.144:60000/devdb]
13:09:28.189 [5253145#qtp-25442933-1] DEBUG org.hibernate.SQL - update deal set remaining_quantity=remaining_quantity-?, updated_on=?, updated_by=? where id=? and remaining_quantity>=?
13:09:28.242 [5253145#qtp-25442933-1] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
13:09:28.243 [5253145#qtp-25442933-1] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
13:09:28.244 [5253145#qtp-25442933-1] DEBUG o.s.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
13:09:28.245 [5253145#qtp-25442933-1] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Flushing Hibernate Session on transaction synchronization
13:09:28.246 [5253145#qtp-25442933-1] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Registering Hibernate Session for deferred close
13:09:30.524 [5253145#qtp-25442933-1] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Opening Hibernate Session
13:09:30.525 [5253145#qtp-25442933-1] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13291529705
13:09:30.534 [5253145#qtp-25442933-1] DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
13:09:30.537 [5253145#qtp-25442933-1] DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
13:09:30.540 [5253145#qtp-25442933-1] DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
13:09:30.543 [5253145#qtp-25442933-1] DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
13:09:30.549 [5253145#qtp-25442933-1] DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
13:09:30.551 [5253145#qtp-25442933-1] DEBUG o.s.orm.hibernate3.HibernateTemplate - Eagerly flushing Hibernate session
13:09:30.552 [5253145#qtp-25442933-1] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades
13:09:30.554 [5253145#qtp-25442933-1] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections
13:09:30.556 [5253145#qtp-25442933-1] DEBUG org.hibernate.engine.Collections - Collection found: [<redacted>.UserOrder.dealOrderSet#<delayed:8>], was: [<unreferenced>] (initialized)
13:09:30.558 [5253145#qtp-25442933-1] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 5 insertions, 0 updates, 0 deletions to 5 objects
13:09:30.559 [5253145#qtp-25442933-1] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
As you can see, two flush operations occurred.. one about halfway through the log (when I reserve) and another at the end when I try to save the order bean. Here is my data sources config.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dealsdbDataSource" />
<property name="packagesToScan" value="<redacted>.common"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.archive.autodetection">class</prop>
<prop key="hibernate.show_sql">${hibernate-show-sql}</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.hbm2ddl.import_files">${hibernate.hbm2ddl.import_files}</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" name="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<constructor-arg ref="sessionFactory" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Also upon investigation, it looks like my flushmode is AUTO in hibernateTemplate.
Any ideas??
Can you please check , if you have declared #Transactional in the orderDao class.
I suppose that could be the cause.
best practice is to have #Transactional at the service layer and not to have any at the DAO layer.
Related
I'm working on a Java web application that uses Spring and Informix database. Server with the web application and with the database are two different machines.
I noticed that one of the SQL queries takes more time to execute on the production server than on the test server. I took a look in the logs and saw that the most time consuming part of the whole transaction takes place between two messages of the Transaction Synchronization Manager.
This is the log entries of the transaction:
2021-02-23 13:04:07,968 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Using transaction object [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject#3f847659]
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Initializing transaction synchronization
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] Getting transaction for [com.prod.service.material.MaterialService.getMaterialIds]
2021-02-23 13:04:07,969 DEBUG [com.prod.service.material.MaterialServiceImpl] getMaterialIds
2021-02-23 13:04:07,969 DEBUG [com.prod.dao.material.MaterialDaoJdbc] getMaterialIds
2021-02-23 13:04:07,969 DEBUG [com.prod.dao.material.MaterialDaoJdbc$MaterialListQuery] RdbmsOperation with SQL […] compiled
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.core.JdbcTemplate] Executing prepared SQL query
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.core.JdbcTemplate] Executing prepared SQL statement […]
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Fetching JDBC Connection from DataSource
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Registering transaction synchronization for JDBC Connection
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Bound value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] to thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:07,972 DEBUG [org.springframework.jdbc.core.StatementCreatorUtils] Setting SQL statement parameter value: column index 1, parameter value [2010], value class [java.lang.String], SQL type 12
*2021-02-23 13:04:07,972 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] bound to thread [http-nio-127.0.0.1-8080-exec-21]
*2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] bound to thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] Completing transaction for [com.prod.service.material.MaterialService.getMaterialIds]
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering beforeCommit synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering beforeCompletion synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Removed value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] from thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering afterCommit synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering afterCompletion synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Clearing transaction synchronization
The SQL query itself is pretty complex and returns around 2000 rows of data.
The Transaction Manager and Data Source are specified as:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>com.informix.jdbc.IfxDriver</value></property>
<property name="url"><value>jdbc:informix-sqli://app.prod.com:11111/prod:informixserver=prod</value></property>
<property name="username"><value>prod</value></property>
<property name="password"><value>1234</value></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
System is:
OS: RHEL Server 7.9
Webserver: Tomcat 9.0.34
Why do we have those two messages from the Transaction Manager? Isn't it a single connection to the data source?
Also, what process can happen between those two messages that takes 6 secs to execute? Is the data being placed somewhere?
I ask because I don't know why it takes longer on the production server whereas on the test server with identical data it takes only 2 secs (between two messages I mentioned above).
Thank you!
EDIT
The correct data source definition is as follows (I copied from the wrong file previously):
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:/comp/env/jdbc/MyDS</value></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
And the context.xml file contains:
<Resource name="jdbc/MyDS"
auth="Container" type="javax.sql.DataSource"
driverClassName="com.informix.jdbc.IfxDriver"
url="jdbc:informix-sqli://app.prod.com:11111/prod:informixserver=prod;ifxIFX_LOCK_MODE_WAIT=20;ifxIFX_XASPEC=Y;"
username="prod" password="prod"
maxActive="100" maxIdle="25" maxWait="10000"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
I have a method in the repository class marked as #Transactional, the aspect is being executed as seen in the stacktrace, but the exception being thrown is "Transaction required exception"
I changed the #Repository annotation to #Component (and it seemd like it fixed this problem in some situations), but it is still happening on the web role.
Here is the stacktrace:
2015-04-13 08:00:56,497 [http-nio-8080-exec-9] WARN es.mycompany.util.filters.MyFilter - Error storing : /admin/online/update
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:37)
at es.mycopmany.dao.MyDAO.updateLastUpdatedTs_aroundBody2(MyDAO.java:36)
at es.mycopmany.dao.MyDAO$AjcClosure3.run(MyDAO.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:70)
at es.mycompany.dao.MyDAO.updateLastUpdatedTs(MyDAO.java:31)
And here is the code throwing the exception:
#Transactional
public void updateLastUpdatedTs(String id, Calendar date) {
Query query = entityManager.createQuery("update MyEntity set lastUpdatedTs = :ts "
+ " where id= :id");
query.setParameter("ts", date);
query.setParameter("id", id);
query.executeUpdate();
}
Transactional annotation comes from org.springframework.transaction.annotation.Transactional
Versions:
Spring: 4.1.5.RELEASE
Hibernate: 4.3.8.Final
Aspectj: 1.8.5
Tomcat 8.0.20
Configurations:
EMF:
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="athena" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
Transactions:
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />
I am going truly nuts with this, any help would be great.
As a note, this all works perfectly fine on my development environment (Windows, Idea Tomcat 8, JDK 8.0.31 (Oracle's), but it raises this error on Amazon EC2 Elasticbeanstalk (Tomcat 8, 64bit Amazon Linux 2015.03, Open JDK 8.0.31 (Tried to use 8.0.40 from Oracle as well)
Edit: A bit more info: The exception is thrown on a Filter, at the end of the whole filter chain.
Here is some debug info before the exception:
2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [MyService.myMethod]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#33f67ee5] for JPA transaction
2015-04-13 14:57:48,580 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#3112368a]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder#3193771b] for key [HikariDataSource (HikariPool-1)] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder#497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#5019da97] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyService.myMethod]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#5019da97] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#33f67ee5] for JPA transaction
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#3193771b] for key [HikariDataSource (HikariPool-1)] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyDao.updateLastUpdatedTs]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
2015-04-13 14:57:48,582 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
2015-04-13 14:57:48,582 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder#4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#7cc8111c] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#7cc8111c] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Completing transaction for [MyDao.updateLastUpdatedTs] after exception: org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
Which actually says, it has created the Transaction, it then joined the transaction (There are two #Transactionals now, one at the service layer, and the other at the DAO layer), and then it rollsback the transaction, due to an exception "Transaction required".
This is nuts.
EDIT
Well, I found this line of debug:
2015-04-13 15:27:44,074 [http-bio-8080-exec-2] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
The values here are: propagation=REQUIRED, isolation=DEFAULT
Seems that, there is a transaction, which was checked as completed, and joining the transaction failed, so it marks it as rollback only, because it could not join it.
I changed the annotation-driven config, just by adding proxy-target-class="true" seems to have fixed the issue in one of our environments (ap-southeast) which is Amazon Shanghai, but as for Europe (eu-west), the problem is still happening. This is a nightmare, all the configurations are exactly the same (it just points to different db & s3)
<tx:annotation-driven mode="aspectj" proxy-target-class="true" transaction-manager="transactionManager" />
SOLUTION:
I finally got something, after all. This fixes it (at least apparently).
Reason:
Apparently it as something to do with spring initialization, and scheduling some tasks before the initialization has finished, and something got messed up.
I set the transactional annotation at the service layer with a REQUIRES_NEW propagation, to force a new transaction to be created.
#Transactional(propagation = Propagation.REQUIRES_NEW)
Removed the #Transactional from the DAO layer.
I also had to make some changes to the connector, incrementing maxThreads and max/min spare threads.
I also changed all my #Scheduled initialization tasks to start 10 minutes after tomcat start
After all this changes, the error went away.
Notes
I also removed the previous change: "proxy-target-class="true"", and it is still working fine, so that weren't really a good fix here, but it might work for you as it did for me on some cases (background tasks).
As as side note, the other change that I had to do to make this work, was to change #Repository to #Component, as some transactions weren't doing writes to the DB on scheduled tasks.
Not an expert in spring, but hope this helps.
Some days ago, reading Spring documentation for a similar issue, I've found:
In particular, you do not need an application server simply for declarative transactions through EJBs. In fact, even if your application server has powerful JTA capabilities, you may decide that the Spring Framework’s declarative transactions offer more power and a more productive programming model than EJB CMT.
AFAIK, in our services, we declare transactions more specific to avoid some of this problems like:
#Transactional
(
propagation = Propagation.REQUIRED,
readOnly = false,
rollbackFor = Throwable.class
)
If your annotations works ONLY in some servers, try to be specific when declaring it in order to cover your transaction scenario. In this way I guess you will achieve same behaviour in all servers.
I just had the very same problem. Turned out, that I tried to use the class-wide defined EntityManager in a background thread created on the fly, and that caused the exception. There were no other error messages regarding this issue, and the stacktrace pointed to query.executeUpdate(), so it was a bit difficult to sort this out. Going back to serial processing made the error vanish.
I think the issue has been solved in latest versions of spring. Just adding the #Transactional annotation on top of my service class worked for me.
I must preserve a ResultSet which was opened within a #Transactional Controller, to be consumed within a MessageConverter. To that end I have configured the following:
MVC Interceptor:
<mvc:interceptors>
<bean class="org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor"
p:sessionFactory-ref="sessionFactory"/>
</mvc:interceptors>
on the SessionFactory bean:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.release_mode">on_close</prop>
....
</props>
</property>
</bean>
within the Controller method:
session.doWork((con) -> { con.setHoldability(HOLD_CURSORS_OVER_COMMIT); });
Yet the PSQLException: This ResultSet is closed. persists. This is a relevant snippet in the log when the transaction commits upon controller method return:
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Registering result set [org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet#5a63c2aa]
DEBUG o.h.e.t.s.AbstractTransactionImpl - committing
TRACE o.h.internal.SessionImpl - Automatically flushing session
TRACE o.h.internal.SessionImpl - before transaction completion
DEBUG o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection
DEBUG o.h.e.t.i.j.JdbcTransaction - re-enabling autocommit
TRACE o.h.e.t.i.TransactionCoordinatorImpl - after transaction completion
TRACE o.h.internal.SessionImpl - after transaction completion
TRACE o.h.internal.SessionImpl - Setting flush mode to: MANUAL
DEBUG o.h.internal.SessionImpl - Disconnecting session
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Releasing JDBC container resources [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl#97bfa5f]
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Closing result set [org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet#5a63c2aa]
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Closing prepared statement [select...]
DEBUG o.h.e.j.i.LogicalConnectionImpl - Releasing JDBC connection
DEBUG o.h.e.j.i.LogicalConnectionImpl - Released JDBC connection
DEBUG o.h.e.j.s.SqlExceptionHelper - could not advance using next() [n/a] org.postgresql.util.PSQLException: This ResultSet is closed.
Is there something more I can do to stop this from happening?
By default Spring will manage the Hibernate session on its own. One of the consequences, as documented in the HibernateTransactionManager Javadoc, is that Spring will explicitly call session.disconnect(), which will invalidate your connection.release_mode=on_close setting. To change this behavior to Hibernate-managed sessions, be sure to set the hibernateManagedSession property on HibernateTransactionManager to true:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:hibernateManagedSession="true" />
Doing it this way is bound to disturb certain mechanisms which assume the default behavior. One of them is transaction management via the TransactionTemplate: no Hibernate session will be automatically created. This can be fixed by explicitly binding a session to the current thread, and is best captured in a subclass of TransactionTemplate:
#Component
public class HibernateTransactionTemplate extends TransactionTemplate
{
#Autowired private SessionFactory sf;
#Autowired #Override public void setTransactionManager(PlatformTransactionManager txm) {
super.setTransactionManager(txm);
}
#Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
final Session ses = sf.openSession();
TransactionSynchronizationManager.bindResource(sf, new SessionHolder(ses));
try { return super.execute(action); }
finally {
ses.close();
TransactionSynchronizationManager.unbindResource(sf);
}
}
}
I am doing a standart getHibernateTemplate().saveOrUpdate() in Spring Hibernate application, but function never returns and does not print any errors.
Debug log is like below.
19:06:07.014 [qtp8540084-26] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
19:06:07.014 [qtp8540084-26] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Found thread-bound Session [org.hibernate.impl.SessionImpl#13ca565] for Hibernate transaction
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [com.mydao.InventoryDAOImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl#13ca565]
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - begin
19:06:07.014 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
19:06:07.014 [qtp8540084-26] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBCDriverManager Connection to [jdbc:hsqldb:file:hsqldb/MyDB]
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - current autocommit status: true
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - disabling autocommit
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [org.hsqldb.jdbc.jdbcConnection#65335b]
19:06:07.030 [qtp8540084-26] DEBUG o.s.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
19:06:07.030 [qtp8540084-26] DEBUG o.s.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction rollback
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl#13ca565]
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - rollback
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - re-enabling autocommit
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - rolled back JDBC Connection
19:06:07.030 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl#13ca565] aftertransaction
19:06:07.030 [qtp8540084-26] DEBUG org.hibernate.impl.SessionImpl - disconnecting session
19:06:07.045 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
19:06:07.045 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
19:06:07.045 [qtp8540084-26] DEBUG o.s.a.f.a.ThrowsAdviceInterceptor - Found handler for exception of type [java.lang.Throwable]: public void org.springframework.flex.core.ExceptionTranslationAdvice.afterThrowing(java.lang.Throwable)throws java.lang.Throwable
19:06:07.077 [qtp8540084-26] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Dispatcher Servlet': assuming HandlerAdapter completed request handling
19:06:07.077 [qtp8540084-26] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
19:06:07.092 [qtp8540084-26] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chainprocessed normally
19:06:07.092 [qtp8540084-26] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter -SecurityContextHolder now cleared, as request processing completed
19:06:07.092 [qtp8540084-26] DEBUG o.s.o.h.s.OpenSessionInViewFilter - Closing single Hibernate Session in OpenSessionInViewFilter
19:06:07.092 [qtp8540084-26] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
Regards.
Edit1: I can use this same method of DAO for other entities successfully. Also, I can update a persistent object of this entity, but i can not "save" or "create" a new one with saveOrUpdate.
This hang can be caused by a deadlock between transactions.
Such deadlocks are often caused by incorrect use of several transactions in a single thread (if transactions A and B are created by a single thread, transaction A waits for transaction B to release a lock, but control flow never reaches the point where B is to be committed).
So, make sure that Spring transaction management is configured properly and that you don't mix Spring-managed transactions with manually managed ones.
Another possible reason is a long-running transaction created by external system. If you have some external systems connected to the database in question (for example, administration tools), make sure that transactions created by these systems are committed.
It can also happen if any of the child entities are lazy fetched.changing to eager fetch will resolve the issue
I'm using Hibernate with OpenSessionInViewInterceptor so that a single Hibernate session will be used for the entire HTTP request (or so I wish). The problem is that Spring-configured transaction boundaries are causing a new session to be created, so I'm running into the following problem (pseudocode):
Start in method marked #Transactional(propagation = Propagation.SUPPORTS, readOnly = false)
Hibernate session #1 starts
Call DAO method to update object foo; foo gets loaded into session cache for session #1
Call another method to update foo.bar, this one is marked #Transactional(propagation = Propagation.REQUIRED, readOnly = false)
Transaction demarcation causes suspension of current transaction synchronization, which temporarily unbinds the current Hibernate session
Hibernate session #2 starts since there's no currently-existing session
Update field bar on foo (loading foo into session cache #2); persist to DB
Transaction completes and method returns, session #1 resumes
Call yet another method to update another field on foo
Load foo from session cache #1, with old, incorrect value of bar
Update field foo.baz, persist foo to DB
foo.bar's old value overwrites the change we made in the previous step
Configuration looks like:
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor" autowire="byName">
<property name="flushModeName">
<value>FLUSH_AUTO</value>
</property>
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="useTransactionAwareDataSource" value="true" />
<property name="mappingLocations">
<list>
<value>/WEB-INF/xml/hibernate/content.hbm.xml</value>
</list>
</property>
<property name="lobHandler">
<ref local="oracleLobHandler" />
</property>
<!--property name="entityInterceptor" ref="auditLogInterceptor" /-->
<property name="hibernateProperties"
ref="HibernateProperties" />
<property name="dataSource" ref="myDataSource" />
</bean>
I've done some debugging and figured out exactly where this is happening, here is the stack trace:
Daemon Thread [http-8080-1] (Suspended (entry into method doUnbindResource in TransactionSynchronizationManager))
TransactionSynchronizationManager.doUnbindResource(Object) line: 222
TransactionSynchronizationManager.unbindResource(Object) line: 200
SpringSessionSynchronization.suspend() line: 115
DataSourceTransactionManager(AbstractPlatformTransactionManager).doSuspendSynchronization() line: 620
DataSourceTransactionManager(AbstractPlatformTransactionManager).suspend(Object) line: 549
DataSourceTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 372
TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(TransactionAttribute, String) line: 263
TransactionInterceptor.invoke(MethodInvocation) line: 101
ReflectiveMethodInvocation.proceed() line: 171
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204
$Proxy14.changeVisibility(Long, ContentStatusVO, ContentAuditData) line: not available
I can't figure out why transaction boundaries (even "nested" ones - though here we're just moving from SUPPORTS to REQUIRED) would cause the Hibernate session to be suspended, even though OpenSessionInViewInterceptor is in use.
When the session is unbound, I see the following in my logs:
[2010-02-16 18:20:59,150] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager Removed value [org.springframework.orm.hibernate3.SessionHolder#7def534e] for key [org.hibernate.impl.SessionFactoryImpl#693f23a2] from thread [http-8080-1]
First, your openSessionInViewInterceptor must have a sessionFactory injected, otherwise it can't do its job:
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
Also, there is a property called singleSession - it is true by default, but debug its value just in case.
Then, if using Spring-MVC, you have to configure the interceptor for the SimpleUrlHandlerMapping (or whichever you are using), so that it can be actually applied:
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
If using anything else, I think you have to define it using <aop> tags (what web framework are you using?)
I have this exact same problem. I had first thought that DB transaction boundaries drove creation of hibernate sessions. After a bit of debugging I realize now that I don't really understand them -- or how they are 'supposed' to be setup.
I'm using spring and a #Transactional service with two associated DAOs. I'm also using the default propagation (REQUIRED) across the board.
public class MyService {
public MyPersonDao personDao; // injected by spring
public MyAddressDao addressDao; // injected by spring
#Transactional
public void create(Person p) {
Address a = addressDao.findOrCreate(p.getAddressData());
boolean inSession = personDao.getHibernateTemplate.contains(a); // false
p.setAddress(adressDao.create();
personDao.store(p); // fails because a is transient
}
}
From what I see in my logs, it looks like function calls through transactional proxies seem to open and close hibernate sessions.