Spring 2 和 JPA 简介(5)

用 Spring DAO 实现域服务

在这一节,您将用 Spring DAO(数据访问对象)API 实现员工信息应用程序的服务接口。

实现 EmployeeService 接口

一旦 Spring 2 引擎知道了如何保持 Employee 和 Address 对象的实例,实现 EmployeeService 接口的任务就变得非常简单。

可以在服务实现中利用 Spring DAO API。Spring DAO 实现了著名的 DAO 设计模式(请参阅 参考资料)。在这个模式中,DAO 提供了一致的数据访问外观。通过传输对象执行数据提取和修改。DAO 封装了实际的数据源,并提供了操作传输对象的方法。

从架构上说,DAO API 隐藏了操作实际数据持久性 API 调用的复杂性。(除了 JPA 之外,Spring 还支持其他 ORM 技术,例如 JDO、Hibernate、iBATIS SQL Maps 和 Apache OJB。)。使用 Spring 的 DAO,可以编写能够轻松适应这些持久性 API 的数据访问代码。

除了对数据持久性 API 的抽象,Spring 的 DAO 支持把各种特定于厂商的数据访问异常映射到一套归档良好的 Spring 数据访问异常。

Spring DAO API 还提供了便于扩展的支持类。通过扩展它们,您可不必再编写烦琐而易于出错的 ORM 数据访问代码。所需的全部编码都封装在基类和支持库中,而且经过全面测试。这些类封装了通常与应用程序逻辑混杂在一起的连接和事务管理代码。在 JPA 支持类的情况下,对 JPA 实体管理器的使用完全封装在支持类中,因此您不必考虑实体管理器和实体管理器工厂的处理。

一些真实的代码可以为您展现 Spring DAO API 的多功能性。清单 10 是 EmployeeService 接口的实现,称为 EmployeeDAO,它使用了 Spring 2 的 JpaDaoSupport 类:

清单 10. 用 Spring 2 的 JPA 支持实现的 EmployeeService 接口
import java.util.List;

import org.springframework.orm.jpa.support.JpaDaoSupport;

public class EmployeeDAO extends JpaDaoSupport implements EmployeeService {

   public Employee findById(long id) {
      return getJpaTemplate()。find(Employee.class, id);
   }
    public List<Employee> findAll() {
       return getJpaTemplate()。find("select e from Employee e");
    }
   public List<Employee> findByEmployeeNumber(String empno) {
      return getJpaTemplate()。find(
      "select e from Employee e where e.empno = ?1", empno);
   }

   public List<Employee> findByAddressStreetName(String street) {
      return getJpaTemplate()。find(
      "select e from Employee e where e.addr.street = ?1", street);
   }

   public List<Employee> findByEmployeeLastName(String lastName) {
      return getJpaTemplate()。find(
      "select e from Employee e where e.lastName = ?1", lastName);
   }

   public List<Employee> findEmployeeWithSalaryOver(double sal) {
      return getJpaTemplate()。find(
      "select e from Employee e where e.salary > ?1", sal);
   }

   public List<Employee> findEmployeeWithCommissionOver(double comm) {
      return getJpaTemplate()。find(
      "select e from Employee e where e.commission > ?1", comm);
   }

   public Employee save(Employee emp) {

      getJpaTemplate()。persist(emp);
      return emp;
   }

   public Employee update(Employee emp) {
      return getJpaTemplate()。merge(emp);
   }

   public void delete(Employee emp) {
      getJpaTemplate()。remove(emp);
         
   }
   
}


在清单 10 中,最值得注意的就是各方法实现中编码极其简单。JpaDaoSupport 类处理了大多数烦琐的日常工作。JpaTemplate 助手类能:

* 隐藏底层的 API 差异
* 转换异常
* 管理 JPA 实体管理器
* 打包事务处理
* 把数据访问标准化成对少数一致(跨全部 Spring DAO 实现)和定义良好的方法的访问

表 2 总结了清单 10 中的 JpaTemplate 方法,这是一种常用的方法:

表 2. EmployeeDAO 中的 JpaTemplate 方法
方法说明
find(Class <T> cls, Object id); 通过实例的主键找到保持的实例。
find(String query); 使用查询字符串找到保持的对象。这个强大的查询语言是 EJB QL 的扩展版本,在 JSR-220 中有完整描述(请参阅 参考资料)。
persist(Object obj); 保存实例到数据库。用 JPA 的说法,它用 JPA 实体管理器保持实例。
merge(Object obj); 用所提供的实例中的信息更新保存的对象实例。
remove(Object obj); 从数据库中删除保持的实例。



在幕后,JpaTemplate 辅助类利用 JPA 实体管理器处理全部操作。辅助类处理数据访问期间例行的实体管理器检索和关闭操作。

在处理某些具体需求时,JpaTemplate 类中的其他方法可能有所帮助。请参考 Spring DAO API 的 JavaDoc 获得更多细节(请参阅 参考资料)。

有了保持 Employee 和 Address 实例的能力和 EmployeeService 的具体实现,接下来就可以根据真实关系数据库进行完整的测试了。


连接 Spring bean

至此,对于 Spring 框架什么时候和如何获得机会去实际处理 POJO,仍然不清楚。谜题的数据访问部分解决了,但仍有两个问题存在:Spring 2 引擎怎么知道要做什么,如何指定要使用哪个关系数据库?

立即就会解决这两个问题;将看到如何向 Spring 引擎提供 bean 连接模板。秘密在于叫作 dwspring-service.xml 的 XML bean 描述符文件。这个 bean 描述符文件是 Spring 2 框架操作概述 中提过的连接蓝本。它描述了 Spring 应用程序中不同的 bean 之间的关系。清单 11 显示了这个文件:

清单 11. dwspring-service.xml bean 描述符
                 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="employeeService" class="com.ibm.dw.spring2.EmployeeDAO">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
   </bean>

   <bean id="entityManagerFactory" class=
   "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource"/>
      <property name="jpaVendorAdapter">
         <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
            <property name="showSql" value="true"/>
            <property name="generateDdl" value="true"/>
            <property name="databasePlatform" 
                    value="oracle.toplink.essentials.platform.database.HSQLPlatform"/>
         </bean>
      </property>
      <property name="loadTimeWeaver">
         <bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver"/>
      </property>
   </bean>

   <bean id="dataSource" 
     class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
                    <property name="url" value="jdbc:hsqldb:mem:dwspring"/>
                    <property name="username" value="sa" />
                    <property name="password" value=" " />
   </bean>

   <bean id="transactionManager" 
     class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
      <property name="dataSource" ref="dataSource"/>
   </bean>

</beans>


要测试 EmployeeDAO 实现的动作,可以使用叫作 HSQLDB 的内存内(请参阅 参考资料)。HSQLDB 的可执行文件是 “有依赖项的 Spring 2” 下载的一部分。

在清单 11 中,专门配置 HSQLDB 实例的行用黑体表示。稍后(在 编写针对 RDBMS 的 DAO 集成测试)中,将看到如何修改这些行,来针对 DB2 Express-C 运行集成测试。

请记住 EmployeeDAO 实际上扩展了 JpaDaoSupport 类。这个类希望在装入的时候被 JPA EntityManagerFactory “插入”。然后它可以用这个工厂得到所有数据访问操作的 JPA EntityManager。

图 6 以图形方式显示 bean 在 dwspring2-service.xml 文件中如何连接在一起:

图 6. bean 连接示意图

实际上,清单 11 是需要由 Spring 2 引擎创建的对象的连接计划,图 6 是这些对象的图表。在清单 11 和图 6 中要注意的一个重要项目就是 EmployeeDAO 如何通过叫作 employeeService 的bean 获得。这个实例把自己的 entityManagerFactory 属性设置成另一个名为 entityManagerFactory的 bean:

<bean id="employeeService" class="com.ibm.dw.spring2.EmployeeDAO">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>


ref="" 标志是对上下文中定义的另一个 bean 的引用 —— 通常是在同一个文件中。


依赖性注入

用外部创建的对象来填充属性,就像刚才做的那样,这叫作 注入——更具体地讲,叫作依赖性注入 (DI),因为被注入的对象通常是接收对象进行正确操作所依赖的事物。DI 在 Spring 中框架中使用得很多。使用 DI,编写组件代码时不需要主动查询或查找依赖服务(例如,查询 EntityManagerFactory)。相反,可以就像依赖服务已经存在一样地编写组件代码,Spring 引擎会在代码执行之前把实际的依赖性注入组件。

依赖性注入的应用程序

如果查看 清单 11 中一直到 entityManagerFactory 连接的部分,您会注意到 Spring 注入了以下依赖项:

* dataSource
* jpaVendorAdapter
* loadTimeWeaver

dataSource bean 是 org.springframework.jdbc.datasource.DriverManagerDataSource 的实例,用 HSQLDB RDBMS 的内存中实例进行了配置。

jpaVendorAdapter 属性通过连接到 Spring 应用程序实际 JPA 实现的 bean 注入。在这个示例中,使用的是 JPA 引用实现,通过 org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter 类来访问。这个类接着需要用 databasePlatform property 进行配置。这个属性被设置成 oracle.toplink.essentials.platform.database.HSQLPlatform,此配置支持对 HSQLDB RDBMS 的访问。这个 bean 的 generateDdl 属性控制着是否生成和执行数据定义语言脚本。如果这个属性设为 true,那么这个 bean 每次装入时,都会重新建立数据库中的方案。为了集成测试的目的,应当保持这个属性为 true。

在 dataSource bean 的配置中,创建了 org.springframework.jdbc.datasource.DriverManagerDataSource 的实例。它的参数设置有:

* HSQLDB 数据库驱动程序
* 创建内存中数据库的 JDBC UR(JDBC URL 的 mem 部分)
* 用户名和口令(对 HSQLDB,默认分别是 sa 和 "")

最后一个 transactionManager bean 是为后面的集成测试配置的。不需要连接这个 bean,因为后面要用的测试基类会按类型查找这个 bean。

至此,您应已对 Spring 2 如何连接 bean 有了一定的连接。您还应了解如何把数据库从 HSQLDB 转换到 DB2 Express-C,这一步要在下一节进行)编写针对 RDBMS 的 DAO 集成测试)。
fig06.gif
2008-03-05_114646.gif
快乐渡过每一天,减肥坚持每一天