Reputation: 1271
We are getting the error
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
The place where it happens is where we override the transaction manager, so that we can react to the doBegin, commit and rollback methods. However, the error occurs in the doBegin where we call the super.doBegin() and before any of our code has actually run. The error is also highly intermittent and uncooperative in terms of happening when we want it to.
I see online many people suggesting that this usually means you have TWO transaction managers defined. I initially rejected this as applying to us because we don't. I double checked. But, not so fast, maybe I do. While our wars only have one tx manager, the app where this is happening...the ONLY app where this is happening...is two wars in the same EAR. Each has it's own spring context defined and it's own txManager. Could they be conflicting? Might this be the source of our troubles?
update -
app-config.xml (at least the parts that might be relevant...I left alot of mundane bean definitions out)
<bean id="dataSource" name="enoteDataSource dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="resourceRef"><value>false</value></property>
<property name="jndiName">
<value>${ds.jndi}</value>
</property>
</bean>
<bean id="sessionFactory" name="sessionFactory enoteSessionFactory" class = "org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource"><ref local="dataSource" /></property>
<property name="packagesToScan">
<list>
<value>gov.usdoj.afms.enote.model.*</value>
</list>
</property>
<bean id="txManager" name="txManager transactionManager" class="gov.usdoj.afms.umc.utils.hibernate.AfmsHibernateTransactionManager">
<property name="sessionFactory" ref="enoteSessionFactory" />
</bean>
<!-- some of the transaction are controlled in code through annotation -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txActionAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<tx:method name="deleteUser" propagation="REQUIRES_NEW" rollback-for="BOException" />
<tx:method name="*" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'fetch' are read-only -->
<tx:method name="fetch*" isolation="READ_UNCOMMITTED"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" rollback-for="Throwable" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.ActionPanelService.*(..))"
advice-ref="txActionAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.AccountInfoService.*(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.PersonalInfoService.*(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.CreateUserService.*(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.UM04Service.*(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.LookupService.removeUsrOrgLvlAsgnT(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.LookupService.addOrUpdateUsrOrgLvlAsgnT(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.LookupService.removeAndAddUsrOrgLvlAsgnT(..))"
advice-ref="txAdvice"/>
<aop:advisor
pointcut="execution(* gov.usdoj.afms.umc.services.St60Service.*(..))"
advice-ref="txAdvice"/>
</aop:config>
<bean id="serviceTarge" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="txManager" />
<property name="target" ref="createUserService" />
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="UM04ServiceTarget" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="txManager" />
<property name="target" ref="UM04Service" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="umcLookups" class="gov.usdoj.afms.umc.application.lookups.UMCLookups" init-method="init" scope="singleton">
<property name="lookupDao" ref="LookupDao"/>
</bean>
<bean id="userSearchServiceBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>gov.usdoj.afms.umc.services.UserSearchService</value>
</property>
<property name="target">
<ref bean="userSearchServiceImpl"/>
</property>
<property name="interceptorNames">
<list>
<value>theLogger</value>
</list>
</property>
</bean>
The txManager override is there so we can set some values to the CLIENT_INFO which allows us to identify a user and module with the transaction so our audit triggers record the info.
@Override
protected void doBegin(Object arg0, TransactionDefinition arg1)
{
super.doBegin(arg0, arg1);
if (!Db2ClientInfo.exists()) {
clearDBProperty();
} else {
setDBProperty(Db2ClientInfo.getClientUserId(), Db2ClientInfo.getClientApplicationId());
}
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
super.doCommit(status);
clearDBProperty();
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
super.doRollback(status);
clearDBProperty();
}
@SuppressWarnings("deprecation")
private void setDBProperty(String uId, String appName) {
Session session = getSessionFactory().getCurrentSession();
Properties props = new Properties();
props.setProperty(WSConnection.CLIENT_ID, uId);
props.setProperty(WSConnection.CLIENT_APPLICATION_NAME, appName);
try {
Connection nativeConn = new SimpleNativeJdbcExtractor().getNativeConnection(session.connection());
if (nativeConn instanceof WSConnection) {
WSConnection wconn = (WSConnection) nativeConn;
wconn.setClientInformation(props);
} else {
logger.error("Connection was NOT an instance of WSConnection so client ID and app could not be set");
}
} catch (Exception e) {
throw new RuntimeException("Cannot set DB parameters!", e);
}
}
/**
* Why clear this? Because we use a connection POOLER and we'd like to clear this info when it is checked into the pool.
*/
private void clearDBProperty() {
setDBProperty("", "");
}
Upvotes: 0
Views: 6085
Reputation: 1271
We think we solved the issue. We, as mentioned in the problem statement, have two WARs in one EAR. Each has one transaction, and one sessionFactory, but they point to the same data source. By splitting them to point at separate data sources, the problem goes away. The WARs are separate apps with separate psring configs et al, so we had assumed they were like separate applications, but evidently they can step on each other in this way because they live in the same EAR. In the long term, we will be splitting them into separate EARs, but for now, pointing them at different data sources (even though the two point to the same DB with the same user etc) solves the problem.
Upvotes: 2
Reputation: 4290
First things first, HibernateTransactionManager.doBegin()
method throws the error in this piece of code:
if (txObject.hasConnectionHolder() &&
!txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. "+
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DataSource, no matter whether Hibernate or JDBC access.");
}
This error is primarily caused due to:
IllegalTransactionStateException
is thrown with the following exception message (hard coded in HibernateTransactionManager.doBegin()):Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
It is a configuration error. Check your entire configuration to figure out the problem.
P.S.- In any case the exception message can be misleading since the HibernateTransactionManager might actually be running within a transaction manager that is not a DataSourceTransactionManager.
Upvotes: 2