Spring DAO入門

摘要

        在J2EE 應用中,業務組件通常使用JDBC API 訪問和改變關係數據庫中的持久化數據。這樣經常導致將持久化代碼和業務邏輯混合在一起--- a bad idea. Data Access Object (DAO) 設計模式通過把持久化邏輯分離到數據訪問類中從而解決了這個問題。
       本篇文章有關於DAO 設計模式的入門 ,重點放在DAO模式的優缺點。然後介紹了Spring 2.0 JDBC/DAO 框架並展示了它如何優雅的解決傳統DAO 設計中的缺點。 

傳統DAO設計

     按照J2EE 核心設計模式( Core J2EE Desgin  Pattern) 這本書中的分類,Data Access Object(DAO) 屬於集成層設計模式。DAO在一個分開的層中封裝了持久化存儲訪問和操作的代碼。本篇文章中設計到的持久存儲指的是RDBMS。
DAO模式是業務邏輯層和持久存儲層之間的抽象層,例如圖1. 業務對象通過數據訪問對象訪問RDBMS(data source)。這個抽象層可以靈活的介入並簡化了應用程序代碼。理想情況下,當改變數據源,比如更換數據庫廠商或者類型,只需要改變數據訪問對象並對業 務對象的影響最小。



圖1 使用DAO之前和之後的對比
現在我已經說明了基本的DAO設計模式,到了寫一些代碼的時候了.下面的例子是一個公司的域模型(domain model).爲了簡單化,界定公司有若干工作在不同部門的僱員,比如銷售,市場和人力資源.因爲簡化的原因,我將只關注一個單一的實體叫做僱員 “Employee”。

面向接口編程(Program to an Interface(P2I))

       DAO設計模式提供的靈活性主要歸因於一個對象設計的最佳實踐(best pratice):面向接口編程。這個規則是指具體對象必須實現一個接口,在調用程序中使用接口而不是具體對象本身。這樣做,你可以很容易的代理到一個不 同的實現而對客戶端的代碼影響最小。
      按照這個規則,我將定義Employee DAO 接口,IEmployeeDAO ,並定義findBySalaryRange()方法。業務組件和DAO的這個方法交互。
import java.util.Map;
public interface IEmployeeDAO {
//被執行的SQL 語句
public String FIND_BY_SAL_RNG = "SELECT EMP_NO, EMP_NAME, "
+ "SALARY FROM EMP WHERE SALARY >= ? AND SALARY <= ?";

//Returns the list of employees who fall into the given salary
//range. The input parameter is the immutable map object
//obtained from the HttpServletRequest. This is an early
//refactoring based on "Introduce Parameter Object"

public List findBySalaryRange(Map salaryMap);
} DAO實現類
已經定義了DAO類,現在必須提供一個DAO的實現類EmployeeDAOImpl
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import com.bea.dev2dev.to.EmployeeTO;

public class EmployeeDAOImpl implements IEmployeeDAO{

public List findBySalaryRange(Map salaryMap)
{
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List empList = new ArrayList();

//Transfer Object for inter-tier data transfer
EmployeeTO tempEmpTO = null;
try{
//DBUtil - helper classes that retrieve connection from pool
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(FIND_BY_SAL_RNG);
pstmt.setDouble(1, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
pstmt.setDouble(2, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
rs = pstmt.executeQuery();
int tmpEmpNo = 0;
String tmpEmpName = "";
double tmpSalary = 0.0D;

while (rs.next()){
tmpEmpNo = rs.getInt("EMP_NO");
tmpEmpName = rs.getString("EMP_NAME");
tmpSalary = rs.getDouble("SALARY");
tempEmpTO = new EmployeeTO(tmpEmpNo,
tmpEmpName,
tmpSalary);
empList.add(tempEmpTO);
}//end while
}//end try
catch (SQLException sqle){
throw new DBException(sqle);
}
//end catch
finally{
try{
if (rs != null){
rs.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (pstmt != null){
pstmt.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (conn != null){
conn.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
}
//end of finally block
return empList;
}//end method findBySalaryRange
} 上面的代碼展示了DAO方法的幾個關鍵點:

  • 封裝了所有的同JDBC API的交互.如果有一個O/R mapping解決方案像Kodo或Hibernate被使用,DAO類可以包裝這些具體產品的API。
  • 包裝了JDBC API 中立的查詢對象,返回給業務層做進一步的處理。
  • 本質上他們是無狀態的。他們單一的目標是供業務對象去訪問和改變持久數據。
  • 在處理底層的JDBC API 或數據庫時,他們捕獲發生的任何錯誤(比如,數據庫不可用,錯誤SQL 語法),並報告爲SQLException.DAO對象把這種錯誤通知給業務對象使用JDBC中立的,定製構建的運行時異常類DBException。
  • 他們釋放池中的數據庫資源像Connection和PreparedStatement對象 ,在ResultSet使用完後,撤銷ResultSet遊標佔有的內存。
因此,DAO層通過抽象底層的數據訪問API,從而提供了一直的數據訪問方式。
構建DAO Factory
DAO Factory 是典型的Factory 設計模式的實現,爲的是讓業務對象能創建和使用具體DAO的實現類。業務對象使用DAO接口而不關心其具體實現類。這種可以依賴倒置的原因是DAO Factory提供了巨大的靈活性。只有DAO接口建立的約定保持不變,可以很容易的去改變DAO的實現(比如,從直接的JDBC 到基於Kodo的O/R Mapping),而不影響客戶端的業務對象。
public class DAOFactory {
private static DAOFactory daoFac;

static{
daoFac = new DAOFactory();
}

private DAOFactory(){}

public DAOFactory getInstance(){
return daoFac;
}

public IEmployeeDAO getEmployeeDAO(){
return new EmployeeDAOImpl();
}
}
和業務組件交互
現在應該看看DAO怎麼在整個應用中使用的時候了,正如前面部分提到的,業務層組件通過和DAO的交互去提取和改變持久業務數據,下面列舉了業務服務組件和DAO的交互:
public class EmployeeBusinessServiceImpl implements
IEmployeeBusinessService {

public List getEmployeesWithinSalaryRange(Map salaryMap){

IEmployeeDAO empDAO = DAOFactory.getInstance()
.getEmployeeDAO();
List empList = empDAO.findBySalaryRange(salaryMap);
return empList;
}
} 這是很清晰的,根本沒有任何對持久化接口的依賴。
問題
DAO設計模式也不是沒有缺點:
  • 代碼重複:
  • 耦合:
  • 資源泄露:
  • 錯誤處理:
  • 脆弱代碼:
讓我們看看怎麼保持DAO設計的優點而克服這些缺點呢?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章