主题
企业架构
SPRING 数据访问对象 (DAO) 框架入门
页面: 1, 2, 3
先识别代码中发生变化的部分,然后将这一部分代码分离出来或者封装起来,就能解决以上所列出的问题。Spring的设计者们已经完全做到了这一点,他们发布了一个超级简洁、健壮的、高度可伸缩的JDBC框架。固定部分(像检索连接、准备声明对象、执行查询和释放数据库资源)已经被一次性地写好,所以该框架的一部分内容有助于消除在传统的基于JDBC的DAO中出现的缺点。
图2显示的是Spring JDBC框架的主要组成部分。业务服务对象通过适当的接口继续使用DAO实现类。JdbcDaoSupport是JDBC数据访问对象的超类。它与特定的数据源相关联。Spring BeanFactory负责获得相应数据源的配置详细信息,并将其与JdbcDaoSupport相关联。这个类最重要的功能就是使子类可以使用 JdbcTemplate对象。
图2. Spring JDBC框架的主要组件
JdbcTemplate是Spring JDBC框架中最重要的类。引用文献中的话:“它简化了JDBC的使用,有助于避免常见的错误。它执行核心JDBC工作流,保留应用代码以提供SQL和提取结果。”这个类通过执行下面的样板任务来帮助分离JDBC 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);
}
}