领先一步
VMware提供培训和认证,以加速你的进步。
了解更多前几天我在阅读Vigil Bose在TSS上发表的文章时,看到了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会话,而无需你必须通过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的Javadoc已经提到的那样),我建议你开始直接使用Session和/或EntityManager API,如果你开始在新的项目中分别使用Hibernate或JPA——记住:Spring试图保持非侵入性,这是一个很好的例子!
[更新:一些小的错别字] [更新:添加了关于无注释异常转换的信息]