SPRING 数据访问对象 (DAO) 框架入门
页面: 1, 2, 3

进入Spring DAO

先识别代码中发生变化的部分,然后将这一部分代码分离出来或者封装起来,就能解决以上所列出的问题。Spring的设计者们已经完全做到了这一点,他们发布了一个超级简洁、健壮的、高度可伸缩的JDBC框架。固定部分(像检索连接、准备声明对象、执行查询和释放数据库资源)已经被一次性地写好,所以该框架的一部分内容有助于消除在传统的基于JDBC的DAO中出现的缺点。

图2显示的是Spring JDBC框架的主要组成部分。业务服务对象通过适当的接口继续使用DAO实现类。JdbcDaoSupport是JDBC数据访问对象的超类。它与特定的数据源相关联。Spring BeanFactory负责获得相应数据源的配置详细信息,并将其与JdbcDaoSupport相关联。这个类最重要的功能就是使子类可以使用 JdbcTemplate对象。

Spring DAO Architecture
图2. Spring JDBC框架的主要组件

JdbcTemplate是Spring JDBC框架中最重要的类。引用文献中的话:“它简化了JDBC的使用,有助于避免常见的错误。它执行核心JDBC工作流,保留应用代码以提供SQL和提取结果。”这个类通过执行下面的样板任务来帮助分离JDBC DAO代码的静态部分:

  • 从数据源检索连接。
  • 准备合适的声明对象。
  • 执行SQL CRUD操作。
  • 遍历结果集,然后将结果填入标准的collection对象。
  • 处理SQLException异常并将其转换成更加特定于错误的异常层次结构。

利用Spring DAO重新编写

既然已基本理解了Spring JDBC框架,现在要重新编写已有的代码。下面将逐步讲述如何解决前几节中提到的问题。

第一步:修改DAO实现类- 现在从JdbcDaoSupport扩展出EmployeeDAOImpl以获得JdbcTemplate。

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.core.JdbcTemplate;

public class EmployeeDAOImpl extends JdbcDaoSupport 
                                     implements IEmployeeDAO{

  public List findBySalaryRange(Map salaryMap){

    Double dblParams [] = {Double.valueOf((String)
            salaryMap.get("MIN_SALARY"))
              ,Double.valueOf((String)
            salaryMap.get("MAX_SALARY"))  
          };
    //The getJdbcTemplate method of JdbcDaoSupport returns an
    //instance of JdbcTemplate initialized with a datasource by the
    //Spring Bean Factory
    JdbcTemplate daoTmplt = this.getJdbcTemplate();
    return daoTmplt.queryForList(FIND_BY_SAL_RNG,dblParams); 
  }
}

在上面的清单中,传入参数映射中的值存储在双字节数组中,顺序与SQL字符串中的位置参数相同。queryForList()方法以包含Map(用列名作为键,一项对应一列)的List(一项对应一行)的方式返回查询结果。稍后我会说明如何返回传输对象列表。

从简化的代码可以明显看出,JdbcTemplate鼓励重用,这大大削减了DAO实现中的代码。JDBC和collection包之间的紧密耦合已经消除。由于JdbcTemplate方法可确保在使用数据库资源后将其按正确的次序释放,所以JDBC的资源耗损不再是一个问题。

另外,使用Spring DAO时,不必处理异常。JdbcTemplate类会处理SQLException,并根据SQL错误代码或错误状态将其转换成特定于Spring异常的层次结构。例如,试图向主键列插入重复值时,将引发DataIntegrityViolationException。然而,如果无法从这一错误中恢复,就无需处理该异常。因为Spring DAO的根异常类DataAccessException是运行时异常类,所以可以这样做。值得注意的是Spring DAO异常独立于数据访问实现。如果实现是由O/R映射解决方案提供,就会抛出同样的异常。

第二步:修改业务服务- 现在业务服务实现了一个新方法setDao(),Spring容器使用该方法传递DAO实现类的引用。该过程称为“设置方法注入(setter injection)”,通过第三步中的配置文件告知Spring容器该过程。注意,不再需要使用DAOFactory,因为Spring BeanFactory提供了这项功能:

public class EmployeeBusinessServiceImpl 
                         implements IEmployeeBusinessService {

  IEmployeeDAO empDAO;

  public List getEmployeesWithinSalaryRange(Map salaryMap){

    List empList = empDAO.findBySalaryRange(salaryMap);
    return empList;
  } 
  public void setDao(IEmployeeDAO empDAO){
    this.empDAO = empDAO;
  }
}

请注意P2I的灵活性;即使极大地改动DAO实现,业务服务实现也只需少量更改。这是由于业务服务现在由Spring容器进行管理。

第三步:配置Bean Factory- Spring bean factory需要一个配置文件进行初始化并启动Spring框架。这个配置文件包含所有业务服务和带Spring bean容器的DAO实现类。除此之外,它还包含用于初始化数据源和JdbcDaoSupport的信息:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
  <!-- Configure Datasource -->
  <bean id="FIREBIRD_DATASOURCE" 
    class="org.springframework.jndi.JndiObjectFactoryBean"> 
    <property name="jndiEnvironment"> 
      <props>
        <prop key="java.naming.factory.initial">
          weblogic.jndi.WLInitialContextFactory
        </prop>
        <prop key="java.naming.provider.url">
          t3://localhost:7001
        </prop>
      </props>
    </property> 
    <property name="jndiName"> 
      <value>
        jdbc/DBPool
      </value> 
    </property>
  </bean>

  <!-- Configure DAO -->
  <bean id="EMP_DAO" class="com.bea.dev2dev.dao.EmployeeDAOImpl">
    <property name="dataSource">
      <ref bean="FIREBIRD_DATASOURCE"></ref>
    </property>
  </bean>

  <!-- Configure Business Service -->
  <bean id="EMP_BUSINESS" 
  class="com.bea.dev2dev.sampleapp.business.EmployeeBusinessServiceImpl">
    <property name="dao">
      <ref bean="EMP_DAO"></ref>
    </property>
  </bean>  
</beans>

这个Spring bean容器通过调用JdbcDaoSupport提供的setDataSource()方法,设置包含DAO实现的数据源对象。

第四步:测试- 最后是编写JUnit测试类。依照Spring的方式,需要在容器外部进行测试。然而,从第三步中的配置文件可以清楚地看到,我们一直在使用WebLogic Server连接池。

package com.bea.dev2dev.business;

import java.util.*;
import junit.framework.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class EmployeeBusinessServiceImplTest extends TestCase {
    private IEmployeeBusinessService empBusiness;
    private Map salaryMap;
    List expResult;

    protected void setUp() throws Exception {
        initSpringFramework();
        initSalaryMap();
        initExpectedResult();
    }
    private void initExpectedResult() {
        expResult = new ArrayList();
        Map tempMap = new HashMap();
        tempMap.put("EMP_NO",new Integer(1));
        tempMap.put("EMP_NAME","John");
        tempMap.put("SALARY",new Double(46.11));
        expResult.add(tempMap);
    }
    private void initSalaryMap() {
        salaryMap = new HashMap();
        salaryMap.put("MIN_SALARY","1");
        salaryMap.put("MAX_SALARY","50");
    }
    private void initSpringFramework() {
      ApplicationContext ac = new FileSystemXmlApplicationContext
                ("C:/SpringConfig/Spring-Config.xml"); 
      empBusiness = 
             (IEmployeeBusinessService)ac.getBean("EMP_BUSINESS");
    }
    protected void tearDown() throws Exception {
    }

    /**
     * Test of getEmployeesWithinSalaryRange method, 
     * of class 
     * com.bea.dev2dev.business.EmployeeBusinessServiceImpl.
     */
    public void testGetEmployeesWithinSalaryRange() {
      List result = empBusiness.getEmployeesWithinSalaryRange
                                        (salaryMap);
      assertEquals(expResult, result);        
    }     
}

页面: 1, 2, 3

下一页 »