那么,您是否仍应使用 Spring 的 HibernateTemplate 和/或 JpaTemplate??

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

前几天我读了 Vigil Bose 在 TSS 上关于 使用 Spring 进行动态路由的文章,看到了 HibernateDaoSupport 类的用法。由于这不再是推荐的从 Spring 使用 Hibernate 的方式,我想我不如再写一篇博客来谈谈。

随着 Spring 2.0 的出现,可以再次开始直接使用 Hibernate Session API 了。问题在于,在使用 Hibernate 或 Spring 提供的其他模板化方法时,是否明智地放弃使用 HibernateTemplate。

使用 Spring XxxTemplates

在 Spring 1.0 中,我们引入了一种处理抛出检查性异常的数据访问 API 的革命性方法。Spring 提供的模板方法与其事务同步管理器以及对运行时异常的广泛使用,使得 2005 年我们创造的 TCFTC (try/catch-finally-try/catch 的缩写) 在数据访问代码中变得完全过时。下面您可以看到 (一个简化且不完全精确的版本) 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)
  • Messaging (JmsTempate)
  • Transaction management (TransactionTemplate)
  • JNDI (JndiTemplate)

模板真的有必要吗?

在使用使用已检查异常(与运行时异常或未检查异常相对)的 API 时,模板会增加很多价值,但也会为您的代码库增加很多一致性。学会了 Spring 的 JdbcTemplate 的人可以很容易地开始使用 Spring 的 JdoTemplate 或 Spring 的 HibernateTemplate——使用这些模板的方法对每个模板来说都是相似的。

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

这些技术使用运行时异常这一事实基本上使得 Spring 模板在这些技术上的等价物变得无用……至少在很大程度上是这样,如果您从代码简化角度来看。如果您使用 Spring HibernateTemplate 纯粹是为了减少执行 Hibernate 数据访问操作所需的代码量,那么您可能会说您不一定需要使用模板!然而,当我们查看上表时,我们可以看到 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(即 Hibernate Session 或 JPA EntityManager)附带的普通 API 时,唯一无法直接获得的是将特定于技术的 JDAO 异常转换为 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 Session,而无需您通过 Hibernate Session。唯一缺失的是异常转换。要也实现这一点,您只需要用 @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(Java5 之前),您仍然可以享受自动异常转换;使用 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 所述),我建议您在新项目开始使用 Hibernate 或 JPA 时直接使用 Session 和/或 EntityManager API——请记住:Spring 试图做到非侵入性,这是另一个很好的例子!

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

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

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

查看所有