Hibernate 有一个叫做上下文会话的功能,其中 Hibernate 本身为每个事务管理一个当前的会话。这大致等同于 Spring 对每个事务的一个Hibernate Session 的同步。相应的 DAO 实现类似于下面的例子,基于普通的 Hibernate API:
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Collection loadProductsByCategory(String category) {
return this.sessionFactory.getCurrentSession()
.createQuery("from test.Product product where product.category=?")
.setParameter(0, category)
.list();
}
}
这种风格类似于 Hibernate 参考文档和示例中的风格,只是将 SessionFactory 保存在一个实例变量中。我们强烈推荐这种基于实例的设置,而不是 Hibernate 的 CaveatEmptor 示例应用程序中老式的静态 HibernateUtil 类。(一般来说,除非绝对必要,否则不要在静态变量中保留任何资源)。
前面的 DAO 例子遵循依赖性注入模式。它很适合于 Spring IoC 容器,就像它针对 Spring 的 HibernateTemplate 编码那样。你也可以在普通的 Java 中设置这样一个 DAO(例如,在单元测试中)。要做到这一点,需要将其实例化,并使用所需的工厂引用调用 setSessionFactory(..)
。作为 Spring Bean 的定义,该 DAO 类似于以下内容:
<beans>
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
</beans>
这种 DAO 风格的主要优点是它只依赖于 Hibernate API。不需要导入任何 Spring 类。从非侵入性的角度来看,这很有吸引力,而且对 Hibernate 开发者来说可能感觉更自然。
然而,DAO 抛出的是普通的 HibernateException(未被选中,所以不必声明或捕获),这意味着调用者可以只将异常视为一般的致命的—除非他们想依赖 Hibernate 自己的异常层次。如果不把调用者与实现策略绑在一起,就不可能捕获特定的原因(比如乐观的锁定失败)。对于那些强烈基于 Hibernate、不需要任何特殊异常处理或两者兼而有之的应用来说,这种权衡可能是可以接受的。
幸运的是,Spring 的 LocalSessionFactoryBean 支持 Hibernate 的 SessionFactory.getCurrentSession()
方法,适用于任何 Spring 事务策略,返回当前 Spring 管理的事务性 Session,即使使用 HibernateTransactionManager。该方法的标准行为仍然是返回与正在进行的JTA事务相关的当前 Session,如果有的话。无论你是使用 Spring 的 JtaTransactionManager、EJB 容器管理的事务(CMT),还是 JTA,这种行为都适用。
总之,你可以在普通 Hibernate API 的基础上实现 DAO,同时仍然能够参与 Spring 管理的事务。