茫茫網海中的冷日
         
茫茫網海中的冷日
發生過的事,不可能遺忘,只是想不起來而已!
 恭喜您是本站第 1671876 位訪客!  登入  | 註冊
主選單

Google 自訂搜尋

Goole 廣告

隨機相片
IMG_60D_00191.jpg

授權條款

使用者登入
使用者名稱:

密碼:


忘了密碼?

現在就註冊!

爪哇咖啡屋 : [轉貼]Design Pattern - Data Access Object

發表者 討論內容
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Design Pattern - Data Access Object

Design Pattern — Data Access Object

發佈日期: 作者:

DAO(Data Access Object) 應該是最基礎且最廣泛運用的一個設計模式了。其適用的情境為:

  • 程式需要利用別的媒介來儲存或取得資料
  • 程式需要能移植到其它儲存媒介
  • 各個儲存媒介之間使用的存取方式不同
  • 希望程式不會受到儲存媒介的變換而修改

DAO 通常看到的範例都會與另外一個物件搭配使用,稱之為 Data Transfer Object 。但是這兩者之間並不一定要綁在一起用。DAO 的重點在提供一個統一的資料存取方式,而回傳的資料可以用 DTO 表示,但也可以用其它方式(例如陣列)來實作。這邊以一個會員系統為例,如何以 DAO 模式來提供會員相關資料的存取。

首先需要設計一個 Interface 來提供資料的存取,這邊為了簡化程式數量,還是按照常見作法搭配 DTO 來使用:

PHP


interface IUserDAO
{
public function create();
public function findById($id);
public function updateUser(User $user);
public function deleteUser(User $user);
}

1
2
3
4
5
6
7
interface IUserDAO
{
     public function create ( ) ;
     public function findById ( $id ) ;
     public function updateUser ( User $user ) ;
     public function
deleteUser ( User $user ) ;
}

有了這個 Interface 之後,我們就可以實作兩種 DAO 分別對應到記憶體與資料庫這兩種不同的儲存媒介:

PHP


class MemoryUserDAO implements IUserDAO
{
public function create()
{
// TODO: Implement create() method.
}
public function findById($id)
{
// TODO: Implement findById() method.
}
public function updateUser(User $user)
{
// TODO: Implement save() method.
}
public function deleteUser(User $user)
{
// TODO: Implement delete() method.
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MemoryUserDAO implements IUserDAO
{
     public function create ( )
     {
         // TODO: Implement create() method.
     }
 

     public function findById ( $id )
     {
         // TODO: Implement findById() method.
     }
 
     public function updateUser ( User $user )
     {
         // TODO: Implement save() method.
     }
 
    
public function deleteUser ( User $user )
     {
         // TODO: Implement delete() method.
     }
}

原文出處: Design Pattern — Data Access Object | C瓜農場
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Object-Relational Mapping

[JDBC][上課整理]Object-Relational Mapping

Object-Relational Mapping是一種常見的設計模式

一個資料庫表格會對應一個java的類別,這個java的類別我們通常稱為VO(Value Object)或是Data Transfer Object(DTO)

用來在client端和server端之間傳遞資料

 

範例中以POJO(Plain-Old-Java-Object)類別來實作business entity(business table)

在POJO中有三大部分

  1. 成員變數 instance variable
  2. get方法 用來抓資料
  3. set方法 用來存資料


public class EmpVO {
private int empno;//變數成員,對應是Database裡的欄位
private String ename;
private java.sql.Date hiredate;
private double salary;
private Integer deptno;
private String title;
public int getEmpno() {//get方法,將值取出
return empno;
}
public void setEmpno(int empno) {//set方法,將值插入
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public java.sql.Date getHiredate() {
return hiredate;
}
public void setHiredate(java.sql.Date hiredate) {
this.hiredate = hiredate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

另外我們還會在針對另一個資料庫的表格存取

設計一個(DAO)Data Access Object類別來對應

原本我們的SQL指令都是四散在程式當中,如果有一天要換資料庫會很麻煩

所以我們會將JDBC資料庫裡面用來跟資料庫做存取的程式碼放在這個類別內



import java.sql.SQLException;
import java.util.*;
public interface IEmpDAO {//DAO上面指派下來的介面
public void getConnection() throws SQLException;//
public int insert(EmpVO emp) throws SQLException;
public int update(EmpVO emp) throws SQLException;
public int delete(int empno) throws SQLException;
public EmpVO findByPrimaryKey(int empno) throws SQLException;
public List<EmpVO> getAll() throws SQLException;
public void closeConn() throws SQLException;
} // end of class IEmpDAO

實作介面的類別



import java.util.*;
import java.sql.*;
public class EmpDAO implements IEmpDAO {//實作IEmpDAO介面
private static final String INSERT_STMT = //SQL insert動態指令
"INSERT INTO employee VALUES (?, ?, ?, ?, ?, ?)";
private static final String UPDATE_STMT = //SQL update動態指令
"UPDATE employee SET ename=?, hiredate=?, salary=?, deptno=?, title=? WHERE empno=?";
private static final String DELETE_STMT = //SQL delete動態指令
"DELETE FROM employee WHERE empno=?";
private static final String GET_ONE_STMT = //SQL select指令
"SELECT empno, ename, hiredate, salary, deptno, title FROM employee WHERE empno=?";
private static final String GET_ALL_STMT = //SQL select指令
"SELECT empno, ename, hiredate, salary, deptno, title FROM employee ORDER BY empno";
Connection conn = null;
public void getConnection() throws SQLException {//overloading getConnection方法
String connUrl = "jdbc:sqlserver://localhost:1433;databaseName=jdbc";
conn = DriverManager.getConnection(connUrl, "sa", "passw0rd");
}
public int insert(EmpVO emp) throws SQLException {//overloading insert方法
int updateCount = 0;
PreparedStatement pstmt = conn.prepareStatement(INSERT_STMT);
pstmt.setInt(1, emp.getEmpno());
pstmt.setString(2, emp.getEname());
pstmt.setDate(3, emp.getHiredate());
pstmt.setDouble(4, emp.getSalary());
pstmt.setInt(5, emp.getDeptno());
pstmt.setString(6, emp.getTitle());
updateCount = pstmt.executeUpdate();
return updateCount;
}
public int update(EmpVO emp) throws SQLException {//overloading update方法
int updateCount = 0;
PreparedStatement pstmt = conn.prepareStatement(UPDATE_STMT);
pstmt.setString(1, emp.getEname());
pstmt.setDate(2, emp.getHiredate());
pstmt.setDouble(3, emp.getSalary());
pstmt.setInt(4, emp.getDeptno());
pstmt.setString(5, emp.getTitle());
pstmt.setInt(6, emp.getEmpno());
updateCount = pstmt.executeUpdate();
return updateCount;
}
public int delete(int empno) throws SQLException {//overloading delete方法
int updateCount = 0;
PreparedStatement pstmt = conn.prepareStatement(DELETE_STMT);
pstmt.setInt(1, empno);
updateCount = pstmt.executeUpdate();
return updateCount;
}
public EmpVO findByPrimaryKey(int empno) throws SQLException {//overloading findByPrimaryKey方法
EmpVO emp = null;
PreparedStatement pstmt = conn.prepareStatement(GET_ONE_STMT);
pstmt.setInt(1, empno);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
emp = new EmpVO();
emp.setEmpno(rs.getInt("empno"));
emp.setEname(rs.getString("ename"));
emp.setHiredate(rs.getDate("hiredate"));
emp.setSalary(rs.getDouble("salary"));
emp.setDeptno(rs.getInt("deptno"));
emp.setTitle(rs.getString("title"));
}
return emp;
}
public List<EmpVO> getAll() throws SQLException {//overloading getAll()方法
EmpVO emp = null;
List<EmpVO> emps = new ArrayList<EmpVO>();
PreparedStatement pstmt = conn.prepareStatement(GET_ALL_STMT);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
emp = new EmpVO();
emp.setEmpno(rs.getInt("empno"));
emp.setEname(rs.getString("ename"));
emp.setHiredate(rs.getDate("hiredate"));
emp.setSalary(rs.getDouble("salary"));
emp.setDeptno(rs.getInt("deptno"));
emp.setTitle(rs.getString("title"));
emps.add(emp);
}
return emps;
}
public void closeConn() throws SQLException {//overloading closeConn()方法
if (conn != null)
conn.close();
}
} // end of class EmpDAO

然而有時候我們會必須要自己寫一個程式來測試輸出是否正確
 



import java.sql.SQLException;
import java.util.*;
public class EmpDAODemo {
public static void main(String[] args) {
IEmpDAO dao = new EmpDAO();//多型
try {
dao.getConnection();
// insert
EmpVO emp1 = new EmpVO();//new一個自定的EmpVO物件等於橫向的列
emp1.setEmpno(1009);
emp1.setEname("Jean Tsao");
emp1.setHiredate(java.sql.Date.valueOf("2015-05-16"));
emp1.setSalary(45600);
emp1.setDeptno(100);
emp1.setTitle("engineer");
int count1 = dao.insert(emp1);//insert資料進SQL
System.out.println("insert " + count1 + " rows");//
System.out.println("---------------------------------");
// update
EmpVO emp2 = new EmpVO();//new另一個自定的EmpVO物件,並將欲更新的資料填入
emp2.setEmpno(1009);
emp2.setEname("Jean Tsao");
emp2.setHiredate(java.sql.Date.valueOf("2015-05-16"));
emp2.setSalary(55600);
emp2.setDeptno(100);
emp2.setTitle("senior engineer");
int count2 = dao.update(emp2);//update資料進SQL
System.out.println("update " + count2 + " rows");
System.out.println("---------------------------------");
// find by PrimaryKey
EmpVO emp3 = dao.findByPrimaryKey(1009);//從primary抓出列資訊印出
System.out.println("empno = " + emp3.getEmpno());
System.out.println("empname = " + emp3.getEname());
System.out.println("hiredate = " + emp3.getHiredate());
System.out.println("salary = " + emp3.getSalary());
System.out.println("deptno = " + emp3.getDeptno());
System.out.println("title = " + emp3.getTitle());
System.out.println("---------------------------------");
// get all emp
List<EmpVO> emps = dao.getAll();//從list抓出所有資訊
for (EmpVO emp : emps) {//for each將資料一筆一筆印出
System.out.print(emp.getEmpno() + ", ");
System.out.print(emp.getEname() + ", ");
System.out.print(emp.getHiredate() + ", ");
System.out.print(emp.getSalary() + ", ");
System.out.print(emp.getDeptno() + ", ");
System.out.print(emp.getTitle() + "\n");
}
System.out.println("---------------------------------");
// delete
int count3 = dao.delete(1009);
System.out.println("delete " + count3 + " rows");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
dao.closeConn();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
} // end of class EmpDAODemo

 

輸出結果

 

 

 


原文出處: [JDBC][上課整理]Object-Relational Mapping | jerry的成長之路 - 點部落
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Java 數據訪問對象(DAO)編程模入門
Java數據訪問對象(DAO)編程模入門
J2EE開發人員使用數據訪問對象(DAO)設計模式把底層的數據訪問邏輯和高層的商務邏輯分開。實現DAO模式能夠更加專注于編寫數據訪問代碼。這篇文章中,Java開發人員Sean C. Sullivan從三個方面討論DAO編程的結構特征:事務劃分,異常處理,日志記錄。

在最近的18個月,我和一個優秀的軟件開發團隊一起工作,開發定制基于WEB的供應鏈管理應用程序.我們的應用程序訪問廣泛的持久層數據,包括出貨狀態,供應鏈制度,庫存,貨物發運,項目管理數據,和用戶屬性等.我們使用JDBC API連接我們公司的各種數據庫平臺,并且在整個應用程序中應用了DAO設計模式.

通過在整個應用程序中應用數據訪問對象(DAO)設計模式使我們能夠把底層的數據訪問邏輯和上層的商務邏輯分開.我們為每個數據源創建了提供CRUD(創建,讀取,更新,刪除)操作的DAO類.

在本文中,我將向你介紹DAO的實現策略以及創建更好的DAO類的技術.我會明確的介紹日志記錄,異常處理,和事務劃分三項技術.你將學在你的DAO類中怎樣把這三種技術結合在一起.這篇文章假設你熟悉JDBC API,SQL和關系性數據庫編程.

我們先來回顧一下DAO設計模式和數據訪問對象.

DAO基礎

DAO模式是標準的J2EE設計模式之一.開發人員使用這個模式把底層的數據訪問操作和上層的商務邏輯分開.一個典型的DAO實現有下列幾個組件:

1. 一個DAO工廠類;

2. 一個DAO接口;

3. 一個實現DAO接口的具體類;

4. 數據傳遞對象(有些時候叫做值對象).

具體的DAO類包含了從特定的數據源訪問數據的邏輯。在下面的這段中你將學到設計和實現數據訪問對象的技術。

事務劃分:

關于DAO要記住的一件重要事情是它們是事務性對象。每個被DAO執行的操作(象創建,更新、或刪除數據)都是和事務相關聯的。同樣的,事務劃分(transaction demarcation)的概念是特別重要的。


事務劃分是在事務界定定義中的方式。J2EE規范為事務劃分描述了兩種模式:編程性事務(programmatic)和聲明性事務(declarative)。下表是對這兩種模式的拆分:

聲明性事務劃分 編程性事務劃分
程序員使用EJB的布署描述符聲明事務屬性程序員擔負編寫事務邏輯代碼的責任。
運行時環境(EJB容器)使用這些屬性來自動的管理事務。應用程序通過一個API接口來控制事務。

我將把注意力集中的編程性事務劃分上。

象前面的介紹一樣,DAOs是一些事務對象。一個典型的DAO要執行象創建、更新、和刪除這的事務性操作。在設計一個DAO時,首先要問自己如下問題:

1、 事務將怎樣開始?

2、 事務將怎樣結束?

3、 那個對象將承擔起動一個事務的責任?

4、 那個對象將承擔結束一個事務的責任?

5、 DAO應該承擔起動和結束事務的責任?

6、 應用程序需要交叉訪問多個DAO嗎?

7、 一個事務包含一個DAO還是多個DAO?

8、 一個DAO包含其它的DAO中的方法嗎?

回答這些問題將有助于你為DAO對象選擇最好的事務劃分策略。對ADO中的事務劃分有兩個主要的策略。一種方法是使用DAO承擔事務劃分的責任;另一種是延期性事務,它把事務劃分到調用DAO對象的方法中。如果你選擇前者,你將要在DAO類中嵌入事務代碼。如果你選擇后者,事務代碼將被寫在DAO類的外部。我們將使用簡單的代碼實例來更好的理解這兩種方法是怎樣工作的。

實例1展示了一個帶有兩種數據操作的DAO:創建(create)和更新(update):


public void createWarehouseProfile(WHProfile profile);
public void updateWarehouseStatus(WHIdentifier id, StatusInfo status);

實例2展示了一個簡單的事務,事務劃分代碼是在DAO類的外部。注意:在這個例子中的調用者把多個DOA操作組合到這個事務中。

tx.begin(); // start the transaction
dao.createWarehouseProfile(profile);
dao.updateWarehouseStatus(id1, status1);
dao.updateWarehouseStatus(id2, status2);
tx.commit(); // end the transaction

這種事務事務劃分策略對在一個單一事務中訪問多個DAO的應用程序來說尤為重要。

你即可使用JDBC API也可以使用Java 事務API(JTA)來實現事務的劃分。JDBC事務劃分比JTA事務劃分簡單,但是JTA提供了更好的靈活性。在下面的這段中,我們會進一步的看事務劃分機制。

使用JDBC的事務劃分

JDBC事務是使用Connection對象來控制的。JDBC的連接接口(java.sql.Connection)提供了兩種事務模式:自動提交和手動提交。Java.sql.Connection為控制事務提供了下列方法:


.public void setAutoCommit(Boolean)
.public Boolean getAutoCommit()
.public void commit()
.public void rollback()

實例3展示怎樣使用JDBC API來劃分事務:

import java.sql.*;
import javax.sql.*;
// ...
DataSource ds = obtainDataSource();
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
// ...
pstmt = conn.prepareStatement(";UPDATE MOVIES ...";);
pstmt.setString(1, ";The Great Escape";);
pstmt.executeUpdate();
// ...
conn.commit();
// ...

使用JDBC事務劃分,你能夠把多個SQL語句組合到一個單一事務中。JDBC事務的缺點之一就是事務范圍被限定在一個單一的數據庫連接中。一個JDBC事務不能夠跨越多個數據庫。接下來,我們會看到怎樣使用JTA來做事務劃分的。因為JTA不象JDBC那樣被廣泛的了解,所以我首先概要的介紹一下JTA。

JTA概要介紹


Java事務API(JTA;Java Transaction API)和它的同胞Java事務服務(JTS;Java Transaction Service),為J2EE平臺提供了分布式事務服務。一個分布式事務(distributed transaction)包括一個事務管理器(transaction manager)和一個或多個資源管理器(resource manager)。一個資源管理器(resource manager)是任意類型的持久化數據存儲。事務管理器(transaction manager)承擔著所有事務參與單元者的相互通訊的責任。下車站顯示了事務管理器和資源管理的間的關系。

JTA事務比JDBC事務更強大。一個JTA事務可以有多個參與者,而一個JDBC事務則被限定在一個單一的數據庫連接。下列任一個Java平臺的組件都可以參與到一個JTA事務中:

.JDBC連接

.JDO PersistenceManager 對象

.JMS 隊列

.JMS 主題

.企業JavaBeans(EJB)

.一個用J2EE Connector Architecture 規范編譯的資源分配器。

使用JTA的事務劃分

要用JTA來劃分一個事務,應用程序調用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個典型的JNDI搜索的UseTransaction對象。


import javax.transaction.*;
import javax.naming.*;
// ...
InitialContext ctx = new InitialContext();
Object txObj = ctx.lookup(";java:comp/UserTransaction";);
UserTransaction utx = (UserTransaction) txObj;

應用程序有了UserTransaction對象的引用之后,就可以象示例5那樣來起動事務。

utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement(";UPDATE MOVIES ...";);
pstmt.setString(1, ";Spinal Tap";);
pstmt.executeUpdate();
// ...
utx.commit();
// ...

當應用程序調用commit()時,事務管理器使用兩段提交協議來結束事務。JTA事務控制的方法:

.javax.transaction.UserTransaction接口提供了下列事務控制方法:

.public void begin()
.public void commit()
.public void rollback()
.public void getStatus()
.public void setRollbackOnly()
.public void setTransactionTimeout(int)

應用程序調用begin()來起動事務,即可調用commit()也可以調用rollback()來結束事務。

使用JTA和JDBC


開發人員經常使用JDBC來作為DAO類中的底層數據操作。如果計劃使用JTA來劃分事務,你將需要一個實現了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅動。實現了這些接口的驅動將有能力參與到JTA事務中。一個XADataSource對象是一個XAConnection對象的工廠。XAConnections是參與到JTA事務中的連接。

你需要使用應用程序服務器管理工具來建立XADataSource對象。對于特殊的指令請參考應用程序服務器文檔和JDBC驅動文檔。

J2EE應用程序使用JNDI來查找數據源。一旦應用程序有了一個數據源對象的引用,這會調用javax.sql.DataSource.getConnection()來獲得數據庫的連接。

XA連接區別于非XA連接。要記住的是XA連接是一個JTA事務中的參與者。這就意味著XA連接不支持JDBC的自動提交特性。也就是說應用程序不必在XA連接上調用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應用程序應該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().

選擇最好的方法

我們已經討論了JDBC和JTA是怎樣劃分事務的。每一種方法都有它的優點,回此你需要決定為你的應用程序選擇一個最適應的方法。 在我們團隊許多最近的對于事務劃分的項目中使用JDBC API來創建DAO類。這DAO類總結如下:

.事務劃分代碼被嵌入到DAO類內部

.DAO類使用JDBC API來進行事務劃分

.調用者沒有劃分事務的方法

.事務范圍被限定在一個單一的JDBC連接

JDBC事務對復雜的企業應用程序不總是有效的。如果你的事務將跨越多個DAO對象或多個數據庫,那么下面的實現策略可能會更恰當:

.用JTA對事務進行劃分

.事務劃分代碼被DAO分開

.調用者承擔劃分事務的責任


.DAO參與一個全局的事務中

JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實現將依賴于你的應用程序的特定需求。

日志記錄和DAO

一個好的DAO實現類將使用日志記錄來捕獲有關它在運行時的行為細節。你可以選擇記錄異常、配置信息、連接狀態、JDBC驅動程序的元數據或查詢參數。日志對開發整個階段都是有益的。我經常檢查應用程序在開發期間、測試期間和產品中的日志記錄。

在這段中,我們將展現一段如何把Jakarta Commaons Logging結合中一個DAO中的例子。在我們開始之前,讓我們先回顧一些基礎知識。

選擇一個日志例庫

許多開發人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個完整的日志系統的的能力。下表列出了Java平臺的日志類庫:

日志類庫開源嗎?URL
Java.util.logging http://java.sun.com/j2ee
Jakarta Log4jhttp://hajarta.apache.org/log4j/
Jakarta Commons Logginghttp:/Jakarta.apache.org/commons/logging.html

Java.util.logging是J2SE1.4平臺上的標準的API。但是,大多數開發人員都認為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越java.util.logging的優點之一就是它支持J2SE1.3和J2SE1.4平臺。


Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個把你的應用程序獨立于日志實現的提取層。使用Commons Logging你能夠通過改變一個配置文件來與下面的日志實現來交換數據。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。

一個日志示例

示例7顯示了在一個DOA類中怎樣使用Jakarta Commons Logging

import org.apache.commons.logging.*;
class DocumentDAOImpl implements DocumentDAO
{
static private final Log log = LogFactory.getLog(DocumentDAOImpl.class);
public void deleteDocument(String id)
{
// ...
log.debug(";deleting document: "; + id);
// ...
try
{
// ... data operations ...
}
catch (SomeException ex)
{
log.error(";Unable to delete document"; ex);
// ... handle the exception ...
}
}
}

日志是評估應用程序的基本部分。如果你在一個DAO中遇到了失敗,日志經常會為理解發生的什么錯誤提供最好的信息。把日志結合到你的DAO中,確保得到調試和解決問題的有效手段。

DAO中的異常處理


我們已經看了事務劃分和日志記錄,并且現在對于它們是怎樣應用于數據訪問對象的有一個深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護性。

在實現DAO模式的時候,要考濾下面的問題:

.在DAO的public接口中的方法將拋出被檢查的異常嗎?

.如果是,將拋出什么樣的檢查性異常?

.在DAO實現類中怎能樣處理異常。

在用DAO模式工作的過程中,我們的團隊為異常處理開發了一組方針。下面的這些方針會很大程度的改善你的DAO:

.DAO方法應該拋出有意義的異常。

.DAO方法不應該拋出java.lang.Exception異常。因為java.lang.Exception太一般化,它不能包含有關潛在問題的所有信息。

.DAO方法不應該拋出java.sql.SQLException異常。SQLException是一個底層的JDBC異常,DAO應用努力封裝JDBC異常而不應該把JDBC異常留給應用程序的其它部分。

.在DAO接口中的方法應該只拋出調用者期望處理的檢查性異常。如果調用者不能用適當的方法來處理異常,考濾拋出不檢查性(運行時run-time)異常。

.如果你的數據訪問代碼捕獲了一個異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。

.使用異常鏈把底層的異常傳遞給高層的某個處理器。

.考濾定義一個標準的DAO異常類。Spring框架提供了一個優秀的預定義的DAO異常類的集合。

看Resources,查看有異常和異常處理技術的更詳細信息。

實現示例:MovieDAO

MoveDAO是一個示范了在這篇文章中所討論的所有技術,包括事務劃分、日志記錄和異常處理。你會在Resources段找到MovieDAO的源代碼。它被分下面的三個包:


.daoexamples.exception
.daoexamples.move
.daoexamples.moviedemo

這個DAO模式的實現由下面的類和接口組成:

.daoexamples.movie.MovieDAOFactory
.daoexamples.movie.MovieDAO
.daoexamples.movie.MovieDAOImpl
.daoexamples.movie.MovieDAOImplJTA
.daoexamples.movie.Movie
.daoexamples.movie.MovieImple
.daoexamples.movie.MovieNotFoundException
.daoexamples.movie.MovieUtil

MovieDAO接口定義了DAO的數據操作。這個接口有如下五個方法:

.public Movie findMovieById(String id)
.public java.util.Collection findMoviesByYear(String year)
.public void deleteMovie(String id)
.public Movie createMovie(String rating,String year,String title)
.public void updateMovie(String id,String rating,String year,String title)


daoexamples.movie包包含了兩個MovieDAO接口的實現。每個實現使用了一個同的事務劃分方法,如下表所示:

 MovieDAOImplMovieDAOImplJTA
實現了MovieDAO接口嗎?YesYes
通過JNDI獲得DataSource嗎?Yes Yes
從一個DataSource獲得java.sql.Connection對象嗎?Yes Yes
DAO界定內部的事務嗎?Yes No
使用JDBC事務嗎?YesNo
使用一個XA DataSource嗎?No Yes
分擔JTA事務嗎?No Yes

MovieDAO 示范應用程序

這個示范應用程序是一個叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個表中的movie數據。

這個servlet示范了把JTA感知的MovieDAO和Java消息服務組合到一個單一的事務中,如示例8所示:

UserTransaction utx = MovieUtil.getUserTransaction();

utx.begin();
batman = dao.createMovie(";R";
";2008";
";Batman Reloaded";);
publisher = new MessagePublisher();
publisher.publishTextMessage(";I’ll be back";);
dao.updateMovie(topgun.getId(),
";PG-13";
topgun.getReleaseYear(),
topgun.getTitle());
dao.deleteMovie(legallyblonde.getId());
utx.commit();

要運行這個范例應用程序,在你的應用程序服務器中配置一個XA 數據源和一個非XA數據源。然后布署daoexamples.ear文件。這個應用程序將運行在任何與J2EE兼容的應用程序服務器。

原文出處: Java數據訪問對象(DAO)編程模入門-ShareBody資訊站
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Building Simple Data Access Layer Using JDBC

Building Simple Data Access Layer Using JDBC

Here's how to build a data access layer with JDBC, including data transfer, database creation, and connecting to a database.

Database Creation

We want to create a simple table for users, we may create it using these fields


id        int
name varchar(200)
password varchar(200)
age int

Data Transfer Object

this layer should contain a simple class called Data Transfer Object(DTO) this object is just a simple mapping to the table, every column in the table would be a member variable in the class.

our aim is to order this layer to create, modify, delete or search for an entity Using simple java objects rather than dealing with SQL statements and other database related commands.

We want to map this table to our java code, we can do so by creating a simple class(bean) that contains the same fields.

To make it more encapsulated we should declare all field variables as private and create acessors (Setters and Getters) in addition to constructors, one of them is default constructor.


public class User {
private Integer id;
private String name;
private String pass;
private Integer age;
}

To map fields correctly, we should consider the NULL value in database. The default value for java primitives is a value like 0 in the case of int so we should provide a new data type that can hold the null value. We can do so by using special type of objects called wrappers like Integer instead on int.

Our final class would be like that:


public class User {
private Integer id;
private String name;
private String pass;
private Integer age;
public User() {
}
public User(String name, String pass, Integer age) {
this.name = name;
this.pass = pass;
this.age = age;
}
public User(Integer id, String name, String pass, Integer age) {
this.id = id;
this.name = name;
this.pass = pass;
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
}

A good practice is to provide default empty constructor, a full constructor and a full constructor without the id parameter.

Connecting to Database

We can facilitate Connecting to database by making a central class for connecting to the database
in this class we would provide connection parameters like database JDBC URL, user name and password as final variables (It'd be better to get them from a properties or XML configuration file)
provide a method to return a Connection object or null if it failed to connect, or it may be better throw a runtime exception in that case


public static final String URL = "jdbc:mysql://localhost:3306/testdb";
public static final String USER = "testuser";
public static final String PASS = "testpass";
/**
* Get a connection to database
* @return Connection object
*/
public static Connection getConnection() {
try {
DriverManager.registerDriver(new Driver());
return DriverManager.getConnection(URL, USER, PASS);
} catch (SQLException ex) {
throw new RuntimeException("Error connecting to the database", ex);
}
}

Also we can include a main method to test this connection the whole class would be like this:


import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* Connect to Database
* @author hany.said
*/
public class ConnectionFactory {
public static final String URL = "jdbc:mysql://localhost:3306/testdb";
public static final String USER = "testuser";
public static final String PASS = "testpass";
/**
* Get a connection to database
* @return Connection object
*/
public static Connection getConnection()
{
try {
DriverManager.registerDriver(new Driver());
return DriverManager.getConnection(URL, USER, PASS);
} catch (SQLException ex) {
throw new RuntimeException("Error connecting to the database", ex);
}
}
/**
* Test Connection
*/
public static void main(String[] args) {
Connection connection = connectionFactory.getConnection();
}
}

Data Access Object

This DAO can do CRUD operations, it can Create, Retreive, Updata, Delete from our table.

The interface of our DAO should be like this:


public interface UserDao {
User getUser();
Set<User> getAllUsers();
User getUserByUserNameAndPassword();
boolean insertUser();
boolean updateUser();
boolean deleteUser();
}

Retreive User

The user can be retreived by any unique field like id or name or mail for example. In this method we're searching for a user by his id. The first step is to create a connection from the connector class then execute the select statement to get the user whose id is 7 we may query using this statement:

SELECT * FROM user WHERE id=7

Just there we made a dynamic statement that takes the id from method parameter

by executing this query we get a result set holding the user or null, we can check for that using the next() method in the Resultset. If returned true, we shall proceed to get user data from the Resultset using data getters. After we fill the user with all the data we return with it. If there is no user with this id or any other Exception happened(like invalid SQL Statement) this method would return null.


public User getUser(int id) {
Connection connection = connectionFactory.getConnection();
try {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user WHERE id=" + id);
if(rs.next())
{
User user = new User();
user.setId( rs.getInt("id") );
user.setName( rs.getString("name") );
user.setPass( rs.getString("pass") );
user.setAge( rs.getInt("age") );
return user;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}

It's more convenient to make a separate method to extract user data from result set as we'd use it in many methods.

The new method would throw SQLException and would be provate to limit access only inside the class:


private User extractUserFromResultSet(ResultSet rs) throws SQLException {
User user = new User();
user.setId( rs.getInt("id") );
user.setName( rs.getString("name") );
user.setPass( rs.getString("pass") );
user.setAge( rs.getInt("age") );
return user;
}

Our method would be modified to use the new method:


public User getUser(int id) {
Connection connection = connectionFactory.getConnection();
try {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user WHERE id=" + id);
if(rs.next())
{
return extractUserFromResultSet(rs);
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}

Login Method

The login would be similar. We want to provide user ans pass instaed of id, this should affect parameter list and query statement. This method would return a valid user if the user name and password are correct, null otherwise as there are many parameters, it's more useful to use PreparedStatement


public User getUserByUserNameAndPassword(String user, String pass) {
Connector connector = new Connector();
Connection connection = connector.getConnection();
try {
PreparedStatement ps = connection.prepareStatement("SELECT * FROM user WHERE user=? AND pass=?");
ps.setString(1, user);
ps.setString(2, pass);
ResultSet rs = ps.executeQuery();
if(rs.next())
{
return extractUserFromResultSet(rs);
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}

Select All Method

This method would return all users, so we should return them in a convenient container like array, but as we can't expect the returned rows count. It's better to use a collection like Set or List:


public Set getAllUsers() {
Connector connector = new Connector();
Connection connection = connector.getConnection();
try {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
Set users = new HashSet();
while(rs.next())
{
User user = extractUserFromResultSet(rs);
users.add(user);
}
return users;
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}

Insert Method

The insert method would take a user as an argument and execute an SQL update statement using the PreparedStatement object.
The executeUpdate method returns number of affected rows, we expect to insert a single row thus means It should return 1, if so we return true, otherwise we return false


public boolean insertUser(User user) {
Connector connector = new Connector();
Connection connection = connector.getConnection();
try {
PreparedStatement ps = connection.prepareStatement("INSERT INTO user VALUES (NULL, ?, ?, ?)");
ps.setString(1, user.getName());
ps.setString(2, user.getPass());
ps.setInt(3, user.getAge());
int i = ps.executeUpdate();
if(i == 1) {
return true;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}


update method

the update method is similar to the insert one
the only change is the SQL statement


public boolean updateUser(User user) {
Connector connector = new Connector();
Connection connection = connector.getConnection();
try {
PreparedStatement ps = connection.prepareStatement("UPDATE user SET name=?, pass=?, age=? WHERE id=?");
ps.setString(1, user.getName());
ps.setString(2, user.getPass());
ps.setInt(3, user.getAge());
ps.setInt(4, user.getId());
int i = ps.executeUpdate();
if(i == 1) {
return true;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}


Delete method

The delete method would use a simple query like
DELETE FROM user WHERE id=7
sending this query with the id parameter would delete this record, returning 1 if it's deleted successfully


public boolean deleteUser(int id) {
Connector connector = new Connector();
Connection connection = connector.getConnection();
try {
Statement stmt = connection.createStatement();
int i = stmt.executeUpdate("DELETE FROM user WHERE id=" + id);
if(i == 1) {
return true;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}


原文出處: Building Simple Data Access Layer Using JDBC - DZone Database
前一個主題 | 下一個主題 | 頁首 | | |



Powered by XOOPS 2.0 © 2001-2008 The XOOPS Project|