I'm using Spring 3.0.6, with Hibernate 3.2.7.GA in a Java-based webapp. I'm declaring transactions with @Transactional
annotations on the controllers (as opposed to in the service layer). Most of the views are read-only.
The problem is, I've got some DAOs which are using JdbcTemplate
to query the database directly with SQL, and they're being called outside of a transaction. Which means they're not reusing the Hibernate SessionFactory
's connection. The reason they're outside the transaction is that I'm using converters on method parameters in the controller, like so:
@Controller
@Transactional
public class MyController {
@RequestMapping(value="/foo/{fooId}", method=RequestMethod.GET)
public ModelAndView get(@PathVariable("fooId") Foo foo) {
// do something with foo, and return a new ModelAndView
}
}
public class FooConverter implements Converter<String, Foo> {
@Override
public Foo convert(String fooId) {
// call FooService, which calls FooJdbcDao to look up the Foo for fooId
}
}
My JDBC DAO relies on SimpleJdbcDaoSupport
to have the jdbcTemplate
injected:
@Repository("fooDao")
public class FooJdbcDao extends SimpleJdbcDaoSupport implements FooDao {
public Foo findById(String fooId) {
getJdbcTemplate().queryForObject("select * from foo where ...", new FooRowMapper());
// map to a Foo object, and return it
}
}
and my applicationContext.xml
wires it all together:
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="FooConverter"/>
<!-- other converters -->
</set>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
FooConverter
(which converts a path variable String
to a Foo
object) gets called before MyController#get()
is called, so the transaction hasn't been started yet. Thus when FooJdbcDAO
is called to query the database, it has no way of reusing the SessionFactory
's connection, and has to check out its own connection from the pool.
So my questions are:
Is there any way to share a database connection between the
SessionFactory
and my JDBC DAOs? I'm usingHibernateTransactionManager
, and from looking at Spring'sDataSourceUtils
it appears that sharing a transaction is the only way to share the connection.If the answer to #1 is no, then is there a way to configure
OpenSessionInViewFilter
to just start a transaction for us, at the beginning of the request? I'm using "on_close
" for thehibernate.connection.release_mode
, so the Hibernate Session and Connection are already staying open for the life of the request.
The reason this is important to me is that I'm experiencing problems under heavy load where each thread is checking out 2 connections from the pool: the first is checked out by hibernate and saved for the whole length of the thread, and the 2nd is checked out every time a JDBC DAO needs one for a query outside of a transaction. This causes deadlocks when the 2nd connection can't be checked out because the pool is empty, but the first connection is still held. My preferred solution is to make all JDBC DAOs participate in Hibernate's transaction, so that TransactionSynchronizationManager
will correctly share the one single connection.