在 Hibernate 的 SessionFactory 和 JDBC DAO 之间共享连接的最佳方式是啥?

Posted

技术标签:

【中文标题】在 Hibernate 的 SessionFactory 和 JDBC DAO 之间共享连接的最佳方式是啥?【英文标题】:What's the best way to share a connection between Hibernate's SessionFactory and a JDBC DAO?在 Hibernate 的 SessionFactory 和 JDBC DAO 之间共享连接的最佳方式是什么? 【发布时间】: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。 【参考方案1】:
    有没有办法在 SessionFactory 和我的 JDBC DAO 之间共享数据库连接?我正在使用 HibernateTransactionManager,从查看 Spring 的 DataSourceUtils 看来,共享事务是共享连接的唯一方法。

--> 你可以在 SessionFactory 和 JdbcTemplate 之间共享数据库连接。您需要做的是在两者之间共享相同的数据源。连接池也在两者之间共享。我在我的应用程序中使用它。

您需要为这两个事务配置 HibernateTransactionManager。

在您现有的包结构中(在 dao 包/层中)添加 JdbcDao 类(具有属性 jdbcTemplate 和带有 getter-setter 的数据源),通过JdbcDao 扩展您的 jdbc 实现类。如果你已经为hibernate配置了hibernateTxManager,你就不需要配置了。

问题是,我有一些 DAO 使用 JdbcTemplate 通过 SQL 直接查询数据库,并且它们在事务之外被调用。这意味着他们没有重用 Hibernate SessionFactory 的连接。

--> 你这里可能错了。我认为您可能正在使用相同的连接,唯一的问题可能在于HibernateTransaction 配置。

检查HibernateTransactionManager javadoc:This transaction manager is appropriate for applications that use a single Hibernate SessionFactory for transactional data access, but it also supports direct DataSource access within a transaction (i.e. plain JDBC code working with the same DataSource). This allows for mixing services which access Hibernate and services which use plain JDBC (without being aware of Hibernate)!

检查我的问题:Using Hibernate and Jdbc both in Spring Framework 3.0

配置:使用当前的 hibernate 类添加 dao 类和服务类,如果您想使用现有配置,请不要为它们制作单独的包。否则在xml配置中配置HibernateTransactionManager并使用@Transactional注解。

你的代码有错误:

@Controller
@Transactional
public class MyController ......

在服务类中使用@Transactional注解(best practice)。

更正:

@Transactional(readOnly = true)
public class FooService implements FooService 

  public Foo getFoo(String fooName) 
    // do something
  

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) 
    // do something
  

【讨论】:

我不明白您对如何配置HibernateTransactionManager 以共享连接的解释。我能看到的唯一方法是 JDBC DAO 是否在事务中。你能举个例子说明你是如何接线的吗?我的上下文如下所示: 我知道有些人认为在服务级别划分事务是最佳实践。就我而言,我在控制器级别执行此操作,因为没有任何服务的异常是可恢复的,因此应该回滚整个请求的事务。但除此之外,我的问题的症结是:我可以在 hibernate 和我的 JDBC DAO 之间共享数据库连接,而不需要它们共享事务吗?您的回答主要涉及包结构,我不明白其中的意义。

以上是关于在 Hibernate 的 SessionFactory 和 JDBC DAO 之间共享连接的最佳方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate基础学习—Hibernate相关API介绍

java使用Myeclipse创建Hibernate项目碰到的诸多问题总结

通过 org.springframework.orm.hibernate4.LocalSessionFactoryBean 创建 HibernateTemplate

Hibernate Session 保存操作是不是与底层数据库事务同步?

easyui-datagrid连接数据库实现分页查询数据

No Hibernate Session bound to thread, and configuration does not allow