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);
}
}
}
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.
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.
I have a Generic Repository with a method named saveList(). The purpose of this method is to take a List and persist it in "chunks" of 500 objects. Unfortunately, I'm getting a "TransactionException: Transaction not successfully started" when I get to the commit.
Everything I've seen says that this is a result of the Spring Transaction Manager. Unfortunately, for this particular method, I need to manually control the transaction.
Relevant code is below:
// from generic non-abstract repository
#Transactional
public void saveList(List<T> objectList) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
int i = 1;
for (T obj : objectList) {
session.save(obj);
//sessionFactory.getCurrentSession().save(obj);
i++;
if (i % 500 == 0) {
session.flush();
//sessionFactory.getCurrentSession().flush();
}
}
if (!tx.wasCommitted()) {
tx.commit();
}
//sessionFactory.getCurrentSession().getTransaction().commit();
}
Configuration from applicationContext.xml:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Any assistance is appreciated.
You're using both declarative and programmatic transaction demarcation. Get rid of the session.beginTransaction() and related method calls. Only use #Transactional.
Use openSession method to get session object and close it when you finished your work. It will work perfectly. For Example
Session session = sessionFactory.openSession();
If you must use a manual transaction, then get rid of the declarative #Transactional option at the top.
I don't really see the point of doing this, since the session is not cleared between the flushes (and will thus need as much memory as if you let Hibernate flush at the end of the transaction), but if you really want to do this, and just want a new transaction just for this method, just annotate the method with
#Transactional(propagation = Propagation.REQUIRES_NEW)
and forget about the Hibernate transaction management.
This will make Spring pause the current transaction (if any), start a new transaction, execute your method and commit/rollback the new transaction, and then resume the paused transaction (if any).
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.