抢先一步
VMware 提供培训和认证,助你加速进步。
了解更多前几天我在 TSS 上读到 Vigil Bose 的一篇文章,看到了 HibernateDaoSupport 类的用法。由于这已不再是使用 Spring 操作 Hibernate 的推荐方式,我想不妨再写一篇博客谈谈这个话题。
随着 Spring 2.0 的到来,可以直接使用 Hibernate Session API 成为可能。问题在于,在使用 Hibernate 或 Spring 的任何其他基于模板的方法时,放弃使用 HibernateTemplate 是否明智。
连接获取:如果事务同步处于活动状态(在使用 Spring 的事务管理基础设施时就是如此),大多数情况下,任何 Spring 模板都在整个线程中使用同一个连接(事情实际上比这要复杂一些,但这会让我们过多地陷入细节)。
参与事务 同样,在使用事务管理功能时,Spring 会自动将任何新连接与当前事务关联起来。这同样取决于当前的传播设置等,但无论如何,你的核心代码不受其影响。
SQL 指定:这(显然)需要你自己完成。理想情况下,SQL 应使用绑定参数,以避免任何 SQL 注入的可能性。参数作为参数传递给 JDBC 模板。
语句创建/执行和结果集迭代:在你指定 SQL 后,Spring 将为你创建语句,设置你可能指定的任何参数,执行语句并为你遍历结果集。
从结果集解析结果:如果你愿意(或者你有复杂的解析需求),可以选择自己解析结果集;或者你可以让 Spring 返回一个基本类型列表,或者仅从结果集中返回一个值。
异常处理与转换:在这里,Spring 会将可能发生的任何异常转换为 Spring 自己的 DataAccessException 异常体系,从而自动将调用代码与正在使用的数据访问技术隔离开来。
连接释放:这是最后一部分,Spring 会释放使用的任何资源。当然,如果事务同步处于活动状态,资源可能不会立即释放。
模板适用于多种 API,例如
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 体系。不过,正如我们稍后将看到的,这很容易解决。
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(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 的 JavaDoc 中已经提到的),如果你在一个新项目上开始使用 Hibernate 或 JPA,我建议你直接使用 Session 和/或 EntityManager API——记住:Spring 尽量做到非侵入性,这是又一个很好的例子!
[更新:小错误] [更新:添加了关于无注解异常转换的信息]