第15章 Spring对各种ORM的集成

15.3 Spring中对其他ORM方案的集成概述

不管是从对各种ORM产品的 集成理论以及集成方式 上看,还是从对各种ORM产品 集成关注点来看,Spring对各种ORM产品的集成几乎是一脉相承的。

所以,对于Spring中其他几种ORM产品的集成情况,没有必要再重复几乎一样的理论和方式,因此以下内容仅作提点,不着更多笔墨。

回顾Spring对各种数据访问技术的集成,我们可以归纳出如下几点。

1. 集成理论和方式上

各种数据访问方式的管理和使用通过相应的模板方法类来统一建模,而具体的数据访问逻辑则统一由相应的回调接口提供,从而将数据访问资源的管理和具体的数据访问逻辑相分离

2. 集成关注点上

Spring对各种数据访问技术的集成主要集中在以下三个关注点上。

  • 数据访问资源管理 ,主要涉及两种管理对象,如下所述。

    • 连接工厂(ConnectionFactory)。 连接工厂代表的是创建数据访问会话资源的统一概念 ,通常可以通过特定的数据访问技术的支持来直接创建它们,也可以通过JNDI等服务获取已经创建并配置好的实例。对于JDBC来说,对应连接工厂概念的实体是DataSource,Hibernate是SessionFactory,iBATIS是SqlMapClient……
    • 连接(Connection)或者说会话资源。 连接是客户端与数据媒介进行数据通信的纽带 ,每次进行数据访问的时候,都需要从指定的连接工厂获得某个连接以完成本次数据访问操作,操作完成后关闭当前连接资源。对于JDBC来说,连接概念对应的是java.sql.Connection,对于Hibernate来说,对应的是Session,对于iBATIS是SqlMapSession…

通常,Spring通过FactoryBean对相应的连接工厂资源的配置和创建进行封装,使得它们能够很好地集成到IoC容器中。对于连接资源的管理,则是由相应的模板方法类来处理。

  • 特定数据访问 异常的转译 ,将这些特定的数据访问异常转译为Spring统一的数据访问异常体系,从而使得客户端可以使用统一的方式透明的处理数据访问异常。

  • 将特定于数据访问技术的 事务管理 ,统一纳入到Spring的事务管理抽象层,使得我们可以用统一的方式来管理事务。最主要的是,通过Spring的事务管理抽象层,我们可以得到EJB2时代只有通过相应的EJBContainer才能获得的声明式事务支持。事务管理是一个大的话题,此处只介绍最初的两个关注点。在此基础上,让我们来快速浏览一遍Spring支持的其他数据访问技术。

15.3.1 Spring对JDO的集成

JDO(Java Data Object,网址为http://java.sun.com/jdo/)是Sun提出来的数据持久化规范。该规范从1.0版本发展到现在的2.0版本,虽然整个发展历程不如Hibernae那样突飞猛进,但也涌现了不少优秀的JDO产品实现,比如KodoJDO、JPOX(http://www.jpox.org/)等。现在JDO规范的发展主要由ApacheJDO(http://db.apache.org/jdo/index.html)开源项目来推动。

更多不做介绍 有兴趣可以看原文

15.3.2 Spring对TopLink的集成

TopLink的命运比较曲折,作为最初由Object People公司开发的几乎是Java界ORM产品鼻祖的Toplink,在20世纪90年代末被短命的WebGain收购之后,没过多久又被Oracle收购,并一直跟随Oracle走到今天,然后在2007年被Oracle开源。但不管怎么说,作为一个商业产品,Toplink还是值得世人称道的。

更多不做介绍 有兴趣可以看原文

15.3.3 Spring对JPA的集成

JPA(Java Persistence API,Java持久化API)是Sun于Java EE 5之后提出的ORM解决方案的统一标准,具体实现由不同提供商提供,包括Hibernate、Toplink等。就好像当年的JDBC标准一样,只不过,JPA是面向ORM的统一。

Spring框架在2.0版本之后提供了对JPA的支持,并且在之后的版本中做进一步的统一和完善。

1. Spring中JPA的资源管理

JPA中的EntityManagerFactory对应数据访问的ConnectionFactory的概念,Spring提供了如下三种方式来配置和获取EntityManagerFactory。

使用LocalEntityManagerFactoryBean。 对于基于JavaSE的独立应用程序或者测试环境下的开发,我们可以使用org.springframework.orm.jpa.LocalEntityManagerFactoryBean来获取相应的EntityManagerFactory。

LocalEntityManagerFactoryBean将默认读取META-INF/persistence.xml配置文件信息,来构建相应的EntityManagerFactory,我们只需要指定相应的persistenceUnitName即可,如下所示:

 
    <bean id="entityManagerFactory"
    	class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    	<property name="persistenceUnitName" value="yourPersistenceUnitName"/>
    </bean>

这种方式应用场景有限,对于更多定制需求,可以考虑使用LocalContainerEntityManagerFactoryBean。

使用LocalContainerEntityManagerFactoryBean。org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean是比较常用的配置和获取EntityManagerFactory的方式。通过它,我们可以指定自定义的配置文件位置、独立的dataSource定义甚至Spring提供的定制字节码转换的loadTimeWeaver实现。

下方代码清单给出了Spring IoC容器内LocalContainerEntityManagerFactoryBean通常的配置代码示例。

 
    
    <bean id="mainDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<property name="url">
    		<value>$(db.main.url)</value>
    	</property>
    	<property name="driverClassName">
    		<value>$(db.main.driver)</value>
    	</property>
    	<property name="username">
    		<value>$(db.main.user)</value>
    	</property>
    	<property name="password">
    		<va1ue>$(db.main.password]</va1ue>
    	</property>
    </bean>
    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    	<property name="dataSource" ref="mainDataSource"/>
    	<property name="loadTimeWeaver">
    		<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeweaver"/>
    	</property>
      <property name="persistenceXmlLocation" value="META-INF/main-persistence.xml"/>
    </bean>
    
 

通过JNDI获取EntityManagerFactory。 我们也可以使用绑定到JNDI的EntityManagerFactory,这对基于Spring的IOC容器的应用来说,也只是配置文件的少许改动,例如:

 
    
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    	<property name="jndiName">
    		<value>persistence/pUnitName</value>
    	</property>
    </bean>
    

当然,Spring2.0之后,我们更喜欢用XSD风格的配置,如下所示

 
    
    <jee:jndi-1ookup id=”entityManagerFactory" jndi-name="persistence/pUnitName"/>
    

相对来说,这种方式更加简洁,描述性也更强。

获取EntityManagerFactory实例之后,我们就可以使用Spring为JPA提供的模板方法类org.springframework.orm.jpa.JpaTemplate进行数据访问了。

JpaTemplate的核心模板方法为Object execute(JpaCallback action),开发人员直接通过org.springframework.orm.jpa.JpaCallback回调接口提供具体的数据访问逻辑即可。资源管理、事务以及异常处理等关注点,则由JpaTemplate模板方法来统一管理。

JpaCallback接口定义为开发人员公开了EntityManager实例,用于具体的基于JPA的数据访问操作,该接口定义如下:

 
    public interface JpaCallback {
    	Object doInJpa(EntityManager em) throws PersistenceException;
    }
    

通过EntityManager,开发人员可以完成任何基于JPA原始API可以完成的工作,而EntityManager获取以及释放的管理问题,则由JpaTemplate来处理。

org.springframework.orm.jpa.JpaOperations接口定义了所有JpaTemplate中可用的数据访问模板方法,除了核心的execute(JpaCallback)模板方法,还有一些便于常用操作的模板方法,具体可以参阅JpaOperations或者JpaTemplate的Javadoc文档。

注意更多有关JpaTemplate使用以及EntityManagerFactory实例配置和获取的信息,请参照Spring2.0之后提供的参考文档。

2. Spring中JPA的异常转译

JpaTemplate模板方法内部同时处理了JPA的数据访问异常到Spring统一异常体系的转译。如果使用JpaTemplate进行数据访问,那么不用关心对于特定于JPA的数据访问异常的处理。即使我们在自己的DAO实现中没有使用JpaTemplate,而是使用JPA的原始API进行数据访问,依然能够得到Spring统一异常体系的好处。

实际上,JpaTemplate内部的异常转译最终是通过org.springframework.orm.jpa.EntityManagerFactoryUtils提供的静态方法convertJpaAccessExceptionIfPossible()完成的,该方法签名定义如下:

 
public static DataAccessExceptionconvertJpaAccessExceptionIfPossible(RuntimeException ex)
    

如果需要,我们同样可以直接使用该工具类提供的这个方法来完成到Spring统一异常体系的转译。

3. JpaDaoSupport

Spring为所有基于JPA进行数据访问的DAO实现类提供了一个统一的基类,即org.springframework.orm.jpa.support.JpaDaoSupport,相应的DAO实现类可以直接继承JpaDaoSupport,以获得JpaTemplate提供的JPA数据访问支持。

当然,JpaDaoSupport允许我们注入EntityManagerFactory或者EntityManager实例。如果愿意,我们也可以直接使用JPA的原始API进行数据访问。

注意:本章的主要内容是Spring通过什么理念和操作方式对各种数据访问进行集成,所以,并没有对各种数据访问技术进行详尽的介绍。实际上,要发挥各种数据访问技术的最大功效,我们依然需要精通使用的数据访问技术才行,而这与集成没有太多关系的。集成只是让事情变得更简单。

15.4 小结

Spring框架为当下Java平台上的各种ORM解决方案提供了统一的集成支持,包括数据访问资源的模板化管理、特定的数据访问异常到Spring异常层次体系的转译,以及稍后将会提到的Spring事务管理支持等。

本章主要针对Hibernate和iBATIS两种目前比较流行的ORM解决方案的Spring集成进行了比较详尽的介绍。鉴于Spring对各种ORM产品的集成一脉相承,对其他ORM产品的集成则顺带提及。在阅读完本章内容后,你应该已经对生活在Spring数据访问层的各种ORM产品的使用了如指掌了。