【发布时间】:2012-08-25 16:03:27
【问题描述】:
我在基于 Java 的 web 应用中使用 Spring 3.0.6 和 Hibernate 3.2.7.GA。我在 controllers (而不是在服务层)上声明带有 @Transactional 注释的事务。大多数视图都是只读的。
问题是,我有一些 DAO 使用 JdbcTemplate 直接使用 SQL 查询数据库,并且它们在事务之外被调用。这意味着他们没有重用 Hibernate SessionFactory 的连接。它们在事务之外的原因是我在控制器中的方法参数上使用转换器,如下所示:
@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
}
}
我的 JDBC DAO 依赖 SimpleJdbcDaoSupport 来注入 jdbcTemplate:
@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
}
}
我的applicationContext.xml 将它们连接在一起:
<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(将路径变量String 转换为Foo 对象)在MyController#get() 被调用之前被调用,因此事务尚未开始。因此当调用FooJdbcDAO查询数据库时,它无法重用SessionFactory的连接,必须从池中检出自己的连接。
所以我的问题是:
有没有办法在
SessionFactory和我的 JDBC DAO 之间共享数据库连接?我正在使用HibernateTransactionManager,从查看 Spring 的DataSourceUtils看来,共享事务是共享连接的唯一方式。如果#1 的答案是否,那么有没有办法配置
OpenSessionInViewFilter在请求开始时为我们启动事务?我将“on_close”用于hibernate.connection.release_mode,因此休眠会话和连接已经在请求的生命周期内保持打开状态。
这对我来说很重要的原因是我在重负载下遇到问题,每个线程都从池中检出 2 个连接:第一个由 hibernate 检出并保存整个线程长度,并且每次 JDBC DAO 需要一个用于事务之外的查询时,都会检查第二个。当由于池为空而无法签出第二个连接时,这会导致死锁,但仍保留第一个连接。我首选的解决方案是让所有 JDBC DAO 参与 Hibernate 的事务,这样TransactionSynchronizationManager 将正确共享一个连接。
【问题讨论】:
-
您是否使用 JdbcDaoSupport 或类似的方法将 JdbcTemplate 注入到 DAO 类中?
-
@jonathan.cone - 是的,我使用的是
SimpleJdbcDaoSupport,并且我在我的Spring上下文中为jdbcTemplate对象定义了一个bean。我刚刚澄清了这个问题。 -
你的事务语义很混乱,为什么不能用@Transactional 注释FooService?当您这样做时,Spring 的事务代理将拦截调用并为 JdbcTemplate 启动一个新事务。在同一个请求中对 Hibernate 的后续调用将登记同一个事务。我认为我们在这里仍然缺少一块。
-
我的事务没有在服务级别划分的原因是我不在乎是否有任何服务抛出异常。相反,我将每个控制器设为
@Transactional,因为没有异常(导致事务回滚)是可恢复的。我想做的就是在 Hibernate 和我的JdbcTemplate之间共享数据库连接 - 使用 DAO。
标签: java hibernate spring-mvc