p01ntbl4nk p01ntbl4nk - 3 months ago 27
Java Question

Could not retrieve pre-bound Hibernate session - Could not obtain transaction-synchronized Session for current thread

I am creating a web application with spring-mvc, spring-security, spring-core, and hibernate.

But I always get this non-error from hibernate session factory.

It is actually throwing HibernateException, but I see on the log it is in debug level, and my application actually continues its execution with no problem.

But I am very curious to understand the reason of the exception raised.

The log shows the following lines.

2016-08-29 13:34:55,310 DEBUG [sg.com.diamond.express.base.dao.impl.HibernateDAOImpl].[doExecute]([328]) [http-nio-8888-exec-4] - Could not retrieve pre-bound Hibernate session
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:325)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:418)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:411)
at sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl.findById(BaseHibernateDaoImpl.java:160)
at sg.com.diamond.express.webadmin.service.impl.ClientProfileServiceImpl.searchByName(ClientProfileServiceImpl.java:48)
at sg.com.diamond.express.webadmin.controller.impl.ClientController.home(ClientController.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:472)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:409)
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:146)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)


This is the code that I have.

DAO Level

This is an abstraction level that I created to minimise the number of boiler-plate codes for DAO, no need to implement the basic c-r-u-d.

BaseDAO Interface

public interface BaseDAO <ID extends Serializable, M extends BaseModel> {
Session getCurrentSession();

M findById(ID id) throws DAOException;
List<M> findByCriteria(M criteria) throws DAOException;
List<M> listAll() throws DAOException;
ID saveObject(M object) throws DAOException;
void saveOrUpdate(M object) throws DAOException;
Query createQuery(String hql) throws DAOException;
Query createSQLQuery(String sql) throws DAOException;
Criteria createCriteria(Class clazz) throws DAOException;
List<M> runQuery(Query query) throws DAOException;
List<M> runQuery(String sql) throws DAOException;
}


BaseDAO Implementation

public class BaseHibernateDaoImpl<ID extends Serializable, M extends BaseModel> extends HibernateTemplate implements BaseDAO<ID, M> {

protected Class<M> modelClass;

@Override
public Session getCurrentSession() {
return this.getSessionFactory().getCurrentSession();
}

public ID saveObject(M object) throws DAOException {
try {
object.setCreator("Batch");
object.setCreatedAt(new Timestamp(new Date().getTime()));
object.setUpdater("Batch");
object.setUpdatedAt(new Timestamp(new Date().getTime()));
return (ID) super.save(object);
} catch (Exception e) {
logger.error(ExceptionUtil.getStackTraces(e), e);
throw new DAOException(e);
}
}

/** all other implementation methods **/


Spring XML Configuration

<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Data Source Configuration
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@192.168.170.36:1521:EXPT2" />
<property name="username" value="EXP_USER" />
<property name="password" value="passw0rd" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="targetDataSource"/>
</bean>

<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate abstract session factory parent
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" abstract="true">
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>

<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate entity configurations
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="expressSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" parent="baseSessionFactory">
<property name="hibernateProperties">
<props merge="true">
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="mappingLocations" value="classpath*:hbm/express/*.hbm.xml"/>
</bean>

<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Transaction Management
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>

<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* sg.com.diamond.express.webadmin.service.*.*.*(..))"/>
<aop:advisor id="serviceTx" advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="search*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="list*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="is*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="false"/>
</tx:attributes>
</tx:advice>

<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Base Dao Definitions
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseExpressDAO" class="sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl" abstract="true">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>


Question

Why does the error happen? and it doesn't break my app, the log level is DEBUG, not ERROR, my app is able to continue properly after that.

Is my configuration correct?

Note

Don't mind the full-xml config, for some reason, I need to use full-xml and none-annotations.

Answer

After mucking around with the aop pointcut expression, I noticed that on a simple batch program that I wrote, where I only have 1 context, I do not have this problem.

This leads me to the answer.

I originally structured my spring xml config as multiple layers.


  • At the top level, at the application context, I declare all the data sources, and the transaction managements.

  • At the second level, the servlet level, each servlet has its own context. At this level, I put all the controller-service-dao configurations.

  • There should be multiple of this "second-level" context, for example, 1 servlet context should handle all RESTful services, where another one handles web-ui.



I've structured it assuming it will take on the transaction management setup in the application context. Sort of like "all servlet context should be able to use the same data-source and transaction-management the same way"

But as it turns out, it doesn't.

When I moved the transaction management piece, including the data-source config, into the servlet context configuration, it all works fine, and the service is now wrapped correctly under transaction.

Thanks.

Answer

The error happens because your aop transaction configuration is not correct. It does not cover the service class from the stack trace. Tweak with the mapping until you stop getting the stack trace. Or maybe better use annotated transaction configuration if possible. The answer to your second question is in the sources of hibernate template:

try {
325         session =     getSessionFactory().getCurrentSession();
326         }
327         catch (HibernateException ex) {
328             logger.debug("Could not retrieve pre-bound Hibernate session", ex);
329         }
330         if (session == null) {
331             session = getSessionFactory().openSession();
332             session.setFlushMode(FlushMode.MANUAL);
333             isNew = true;
334         }
Comments