那么,你是否还应该使用Spring的HibernateTemplate和/或JpaTemplate呢?

工程 | Alef Arendsen | 2007年6月26日 | ...

前几天我在阅读Vigil Bose在TSS上发表的文章时,看到了HibernateDaoSupport类的用法。由于这不再是Spring中使用Hibernate的推荐方式,我认为我最好以后再写一篇博客。

随着Spring 2.0的出现,可以直接再次使用Hibernate Session API成为可能。问题是,在使用Hibernate或Spring的其他基于模板的方法时,放弃使用HibernateTemplate是否明智。

使用Spring XxxTemplates

在Spring 1.0中,我们引入了一种革命性的方法来处理抛出已检查异常的数据访问API。Spring提供的模板方法及其事务同步管理器以及对运行时异常的大量使用,使得数据访问代码中常见的TCFTC(即try/catch-finally-try/catch,我们在2005年创造的缩写)完全过时。下面你可以看到(一个简化版本,并非完全精确的版本)Spring的模板方法为你做了什么(以及你否则必须编写的特定代码片段)。template.png

获取连接:如果事务同步处于活动状态(如果使用Spring的事务管理基础设施,则通常如此),大多数情况下,任何Spring模板都使用同一连接贯穿整个线程(实际上比这更复杂,但这会让我们陷入过于详细的细节)。

参与事务 同样,当使用事务管理功能时,Spring会自动将任何新连接与当前事务关联。这同样取决于当前的传播设置等等,但无论如何,你的核心代码都不会受到影响。

SQL规范:这(显然)是你自己必须做的。SQL理想情况下使用绑定参数,以避免发生任何SQL注入的机会。参数作为参数传递给JDBC模板。

创建/执行语句和迭代结果集:指定SQL后,Spring将为你创建语句,设置你可能指定的任何参数,执行它并为你循环遍历结果集。

解析结果集中的结果:你可以选择自己解析结果集(如果你有复杂的解析需求),或者让Spring返回一个基本类型列表,或者只是从结果集中返回一个值。

处理和转换异常:这是Spring将可能发生的任何异常转换为Spring自己的DataAccessException层次结构的地方,自动地将调用代码与正在使用的数据访问技术隔离开。

释放连接:这是Spring释放任何使用的资源的最后一步。当然,如果事务同步处于活动状态,资源可能不会立即释放。

模板可用于多个API,例如:

  • JDBC (JdbcTemplate)
  • Hibernate (HibernateTemplate)
  • iBatis (SqlMapClientTemplate)
  • JDO (JdoTemplate)
  • TopLink (TopLinkTemplate)
  • 消息传递 (JmsTempate)
  • 事务管理 (TransactionTemplate)
  • JNDI (JndiTemplate)

模板真的有必要吗?

当使用使用已检查异常(与运行时异常或未检查异常相反)的API时,模板会增加很多价值,但也会为你的代码库增加很多一致性。学习过Spring的JdbcTemplate的人可以很容易地开始使用Spring的JdoTemplate或Spring的HibernateTemplate——使用它们的方法对每个模板都类似。

Spring模板方法最明显的影响是减少了例如JDBC的代码量。这主要是因为已检查异常在模板内部被转换为运行时异常,从而无需在你的主代码中捕获异常。其他原因是透明的资源管理和与当前正在运行的事务的自动同步。当然,很容易更改框架以原生使用运行时异常,而不是Spring必须这样做,例如Hibernate从3.0版本开始就已开始这样做。Hibernate并不是唯一这样做的技术——Java Persistence API也使用运行时异常。

这些技术使用运行时异常的事实实际上使得这些技术的Spring模板等效物变得无用……至少在很大程度上如此,如果你从简化代码的角度来看。如果你纯粹是为了减少执行Hibernate数据访问操作所需的代码量而使用Spring HibernateTemplate,你会说你并不一定要使用模板!然而,当查看上表时,我们可以看到Spring在幕后做了比你想象的更多工作。

除了部分简化错误处理问题(我们仍然必须将特定于数据访问技术的异常转换为Spring的DataAccessExceptions)之外,事务管理和资源管理问题也得到了解决,方法是底层数据访问技术中的几个更改。让我们更详细地看看这些方面。

资源管理 自Hibernate 3.0.1(以及从Java Persistence API首次发布之日起)以来,Spring能够管理底层资源,而无需你必须通过这些技术的任何可用模板。这意味着即使你直接使用Hibernate API(例如通过SessionFactory.getCurrentSession()),你仍然可以使用Spring管理的Hibernate Session。对于通过JPA EntityManagerFactory获得的EntityManager也是如此。这是你不再需要使用Spring的HibernateTemplate即可获得集成体验的另一个原因。

事务管理 现在Spring能够为你处理底层资源,而无需你必须通过模板,Spring还能够将资源与获取资源时正在进行的任何事务同步。这意味着事务管理问题也得到了解决,而无需你必须通过模板。同样,这意味着我们不再需要使用Spring的HibernateTemplate。

错误处理 当你使用Hibernate或JPA提供的纯API(即Hibernate Session或JPA EntityManager)时,唯一不可直接使用的是将特定于技术的数据库访问异常转换为Spring DataAccessException层次结构的异常转换。然而,正如我们很快就会看到的,我们可以很容易地解决这个问题。

放弃模板

那么,如果我们例如不使用HibernateTemplate,情况会如何呢?展示工作原理非常简单。我们首先做的就是直接使用Session API,而不是HibernateTemplate。要访问Hibernate Session,我们需要SessionFactory,它将像往常一样被注入。

public class HibernateAccountRepository implements AccountRepository {

	private SessionFactory factory;
	
	public HibernateAccountRepository(SessionFactory factory) {
		this.factory = factory;
	}
	
	public Account loadAccount(String username) {
		return (Account)factory.getCurrentSession()
		    .createQuery("from Account acc where acc.name = :name")
		    .setParameter("name", "Alef").uniqueResult();
	}
}

以下是我们将用于组装应用程序的XML。正如你所看到的,我们当然仍然使用Spring设置Hibernate的方式(使用LocalSessionFactoryBean)。


<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<!-- the works -->
</bean>

<bean id="accountRepo" class="com.mycompany.HibernateAccountRepository">
	<constructor-arg ref="sessionFactory"/>
</bean>

现在,正如我之前所说,由于Hibernate 3.0.1中的一个小更改,Spring能够为你管理Hibernate会话,而无需你必须通过Hibernate会话。缺少的一件事是异常转换。要启用该功能,你只需要使用@Repository注释(由Spring提供)注释存储库,并使用后处理器启用异常转换。


@Repository // from org.springframework.stereotype
public class HibernateAccountRepository implements AccountRepository {

	// see above for full impl...
}


<!-- for the other beans in the configuration, see above -->

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

后处理器将自动识别@Repository注释并指示Spring为此bean启用异常转换。使用代理实现这一点的事实与讨论无关。

注意,使用Java Persistence API (JPA)的存储库也是如此。事实上,你甚至不需要更改后处理器或注释。

如果你在不支持注释的环境中使用Hibernate(Java 5之前),你仍然可以使用AOP享受自动异常转换。首先声明一个异常转换器,然后声明一段AOP配置,如下所示


<bean id=“persistenceExceptionInterceptor
    class=“org.springframework.dao.support.PersistenceExceptionTranslationInterceptor"/>

<aop:config>
    <aop:advisor pointcut=“execution(* *..*Repository+.*(..))" 
                          advice-ref=“persistenceExceptionInterceptor" />
</aop:config>

此切点匹配任何实现Repository接口的类(更准确地说,是结尾为Repository的接口)。

真正的问题是:选择哪种方法?

要使用典型的咨询师的答案来回答:“这取决于情况”。让我告诉你,我个人更喜欢不用HibernateTemplate和JpaTemplate,因为我认为它们不再提供足够的价值。出于一致性的目的,你可以认为在整个过程中选择基于模板的方法会让你处于所有地方都类似的情况;然而,你仍然必须学习Hibernate的工作原理,对于更复杂的情况,你可能仍然想要直接使用Session API。请注意,如果你使用HibernateTemplate(通过Spring的HibernateCallback),这仍然是可能的。

因此,简而言之(正如HibernateTemplateJpaTemplate的Javadoc已经提到的那样),我建议你开始直接使用Session和/或EntityManager API,如果你开始在新的项目中分别使用Hibernate或JPA——记住:Spring试图保持非侵入性,这是一个很好的例子!

[更新:一些小的错别字] [更新:添加了关于无注释异常转换的信息]

获取Spring新闻通讯

通过Spring新闻通讯保持联系

订阅

领先一步

VMware提供培训和认证,以加速你的进步。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

查看Spring社区中所有即将举行的活动。

查看全部