http://www.javaeye.com/topic/157685 轉(zhuǎn)自
J2EE開發(fā)人員使用數(shù)據(jù)訪問對(duì)象(DAO)設(shè)計(jì)模式把底層的數(shù)據(jù)訪問邏輯和高層的商務(wù)邏輯分開.實(shí)現(xiàn)DAO模式能夠更加專注于編寫數(shù)據(jù)訪問代碼.
我們先來回顧一下DAO設(shè)計(jì)模式和數(shù)據(jù)訪問對(duì)象.
DAO基礎(chǔ)
DAO模式是標(biāo)準(zhǔn)的J2EE設(shè)計(jì)模式之一.開發(fā)人員使用這個(gè)模式把底層的數(shù)據(jù)訪問操作和上層的商務(wù)邏輯分開.一個(gè)典型的DAO實(shí)現(xiàn)有下列幾個(gè)組件:
1. 一個(gè)DAO工廠類;
2. 一個(gè)DAO接口;
3. 一個(gè)實(shí)現(xiàn)DAO接口的具體類;
4. 數(shù)據(jù)傳遞對(duì)象(有些時(shí)候叫做值對(duì)象).
具體的DAO類包含了從特定的數(shù)據(jù)源訪問數(shù)據(jù)的邏輯。在下面的這段中你將學(xué)到設(shè)計(jì)和實(shí)現(xiàn)數(shù)據(jù)訪問對(duì)象的技術(shù)。
事務(wù)劃分:
關(guān)于DAO要記住的一件重要事情是它們是事務(wù)性對(duì)象。每個(gè)被DAO執(zhí)行的操作(象創(chuàng)建,更新、或刪除數(shù)據(jù))都是和事務(wù)相關(guān)聯(lián)的。同樣的,事務(wù)劃分(transaction demarcation)的概念是特別重要的。
事務(wù)劃分是在事務(wù)界定定義中的方式。J2EE規(guī)范為事務(wù)劃分描述了兩種模式:編程性事務(wù)(programmatic)和聲明性事務(wù)(declarative).下表是對(duì)這兩種模式的拆分:
聲明性事務(wù)劃分 編程性事務(wù)劃分
程序員使用EJB的布署描述符聲明事務(wù)屬性 程序員擔(dān)負(fù)編寫事務(wù)邏輯代碼的責(zé)任。
運(yùn)行時(shí)環(huán)境(EJB容器)使用這些屬性來自動(dòng)的管理事務(wù)。 應(yīng)用程序通過一個(gè)API接口來控制事務(wù)。
我將把注意力集中的編程性事務(wù)劃分上。
象前面的介紹一樣,DAOs是一些事務(wù)對(duì)象。一個(gè)典型的DAO要執(zhí)行象創(chuàng)建、更新、和刪除這的事務(wù)性操作。在設(shè)計(jì)一個(gè)DAO時(shí),首先要問自己如下問題:
1、 事務(wù)將怎樣開始?
2、 事務(wù)將怎樣結(jié)束?
3、 那個(gè)對(duì)象將承擔(dān)起動(dòng)一個(gè)事務(wù)的責(zé)任?
4、 那個(gè)對(duì)象將承擔(dān)結(jié)束一個(gè)事務(wù)的責(zé)任?
5、 DAO應(yīng)該承擔(dān)起動(dòng)和結(jié)束事務(wù)的責(zé)任?
6、 應(yīng)用程序需要交叉訪問多個(gè)DAO嗎?
7、 一個(gè)事務(wù)包含一個(gè)DAO還是多個(gè)DAO?
8、 一個(gè)DAO包含其它的DAO中的方法嗎?
回答這些問題將有助于你為DAO對(duì)象選擇最好的事務(wù)劃分策略。對(duì)ADO中的事務(wù)劃分有兩個(gè)主要的策略。一種方法是使用DAO承擔(dān)事務(wù)劃分的責(zé)任;另一種是延期性事務(wù),它把事務(wù)劃分到調(diào)用DAO對(duì)象的方法中。如果你選擇前者,你將要在DAO類中嵌入事務(wù)代碼。如果你選擇后者,事務(wù)代碼將被寫在DAO類的外部。我們將使用簡單的代碼實(shí)例來更好的理解這兩種方法是怎樣工作的。
實(shí)例1展示了一個(gè)帶有兩種數(shù)據(jù)操作的DAO:創(chuàng)建(create)和更新(update):
public void createWarehouseProfile(WHProfile profile);
public void updateWarehouseStatus(WHIdentifier id, StatusInfo status);
實(shí)例2展示了一個(gè)簡單的事務(wù),事務(wù)劃分代碼是在DAO類的外部。注意:在這個(gè)例子中的調(diào)用者把多個(gè)DOA操作組合到這個(gè)事務(wù)中。
tx.begin(); // start the transaction
dao.createWarehouseProfile(profile);
dao.updateWarehouseStatus(id1, status1);
dao.updateWarehouseStatus(id2, status2);
tx.commit(); // end the transaction
這種事務(wù)事務(wù)劃分策略對(duì)在一個(gè)單一事務(wù)中訪問多個(gè)DAO的應(yīng)用程序來說尤為重要。
你即可使用JDBC API也可以使用Java 事務(wù)API(JTA)來實(shí)現(xiàn)事務(wù)的劃分。JDBC事務(wù)劃分比JTA事務(wù)劃分簡單,但是JTA提供了更好的靈活性。在下面的這段中,我們會(huì)進(jìn)一步的看事務(wù)劃分機(jī)制。
使用JDBC的事務(wù)劃分
JDBC事務(wù)是使用Connection對(duì)象來控制的。JDBC的連接接口(java.sql.Connection)提供了兩種事務(wù)模式:自動(dòng)提交和手動(dòng)提交。Java.sql.Connection為控制事務(wù)提供了下列方法:
.public void setAutoCommit(Boolean)
.public Boolean getAutoCommit()
.public void commit()
.public void rollback()
實(shí)例3展示怎樣使用JDBC API來劃分事務(wù):
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事務(wù)劃分,你能夠把多個(gè)SQL語句組合到一個(gè)單一事務(wù)中。JDBC事務(wù)的缺點(diǎn)之一就是事務(wù)范圍被限定在一個(gè)單一的數(shù)據(jù)庫連接中。一個(gè)JDBC事務(wù)不能夠跨越多個(gè)數(shù)據(jù)庫。接下來,我們會(huì)看到怎樣使用JTA來做事務(wù)劃分的。因?yàn)镴TA不象JDBC那樣被廣泛的了解,所以我首先概要的介紹一下JTA。
JTA概要介紹
Java事務(wù)API(JTA Java Transaction API)和它的同胞Java事務(wù)服務(wù)(JTS Java Transaction Service),為J2EE平臺(tái)提供了分布式事務(wù)服務(wù)。一個(gè)分布式事務(wù)(distributed transaction)包括一個(gè)事務(wù)管理器(transaction manager)和一個(gè)或多個(gè)資源管理器(resource manager)。一個(gè)資源管理器(resource manager)是任意類型的持久化數(shù)據(jù)存儲(chǔ)。事務(wù)管理器(transaction manager)承擔(dān)著所有事務(wù)參與單元者的相互通訊的責(zé)任。下車站顯示了事務(wù)管理器和資源管理的間的關(guān)系。
JTA事務(wù)比JDBC事務(wù)更強(qiáng)大。一個(gè)JTA事務(wù)可以有多個(gè)參與者,而一個(gè)JDBC事務(wù)則被限定在一個(gè)單一的數(shù)據(jù)庫連接。下列任一個(gè)Java平臺(tái)的組件都可以參與到一個(gè)JTA事務(wù)中:
.JDBC連接
.JDO PersistenceManager 對(duì)象
.JMS 隊(duì)列
.JMS 主題
.企業(yè)JavaBeans(EJB)
.一個(gè)用J2EE Connector Architecture 規(guī)范編譯的資源分配器。
使用JTA的事務(wù)劃分
要用JTA來劃分一個(gè)事務(wù),應(yīng)用程序調(diào)用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個(gè)典型的JNDI搜索的UseTransaction對(duì)象。
import javax.transaction.*;
import javax.naming.*;
// ...
InitialContext ctx = new InitialContext();
Object txObj = ctx.lookup("java:comp/UserTransaction");
UserTransaction utx = (UserTransaction) txObj;
應(yīng)用程序有了UserTransaction對(duì)象的引用之后,就可以象示例5那樣來起動(dòng)事務(wù)。
utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement("UPDATE MOVIES ...");
pstmt.setString(1, "Spinal Tap");
pstmt.executeUpdate();
// ...
utx.commit();
// ...
當(dāng)應(yīng)用程序調(diào)用commit()時(shí),事務(wù)管理器使用兩段提交協(xié)議來結(jié)束事務(wù)。
JTA事務(wù)控制的方法
.javax.transaction.UserTransaction接口提供了下列事務(wù)控制方法:
.public void begin()
.public void commit()
.public void rollback()
.public void getStatus()
.public void setRollbackOnly()
.public void setTransactionTimeout(int)
應(yīng)用程序調(diào)用begin()來起動(dòng)事務(wù),即可調(diào)用commit()也可以調(diào)用rollback()來結(jié)束事務(wù)。
使用JTA和JDBC
開發(fā)人員經(jīng)常使用JDBC來作為DAO類中的底層數(shù)據(jù)操作。如果計(jì)劃使用JTA來劃分事務(wù),你將需要一個(gè)實(shí)現(xiàn)了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅(qū)動(dòng)。實(shí)現(xiàn)了這些接口的驅(qū)動(dòng)將有能力參與到JTA事務(wù)中。一個(gè)XADataSource對(duì)象是一個(gè)XAConnection對(duì)象的工廠。XAConnections是參與到JTA事務(wù)中的連接。
你需要使用應(yīng)用程序服務(wù)器管理工具來建立XADataSource對(duì)象。對(duì)于特殊的指令請(qǐng)參考應(yīng)用程序服務(wù)器文檔和JDBC驅(qū)動(dòng)文檔。
J2EE應(yīng)用程序使用JNDI來查找數(shù)據(jù)源。一旦應(yīng)用程序有了一個(gè)數(shù)據(jù)源對(duì)象的引用,這會(huì)調(diào)用javax.sql.DataSource.getConnection()來獲得數(shù)據(jù)庫的連接。
XA連接區(qū)別于非XA連接。要記住的是XA連接是一個(gè)JTA事務(wù)中的參與者。這就意味著XA連接不支持JDBC的自動(dòng)提交特性。也就是說應(yīng)用程序不必在XA連接上調(diào)用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應(yīng)用程序應(yīng)該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().
選擇最好的方法
我們已經(jīng)討論了JDBC和JTA是怎樣劃分事務(wù)的。每一種方法都有它的優(yōu)點(diǎn),回此你需要決定為你的應(yīng)用程序選擇一個(gè)最適應(yīng)的方法。
在我們團(tuán)隊(duì)許多最近的對(duì)于事務(wù)劃分的項(xiàng)目中使用JDBC API來創(chuàng)建DAO類。這DAO類總結(jié)如下:
.事務(wù)劃分代碼被嵌入到DAO類內(nèi)部
.DAO類使用JDBC API來進(jìn)行事務(wù)劃分
.調(diào)用者沒有劃分事務(wù)的方法
.事務(wù)范圍被限定在一個(gè)單一的JDBC連接
JDBC事務(wù)對(duì)復(fù)雜的企業(yè)應(yīng)用程序不總是有效的。如果你的事務(wù)將跨越多個(gè)DAO對(duì)象或
多個(gè)數(shù)據(jù)庫,那么下面的實(shí)現(xiàn)策略可能會(huì)更恰當(dāng)。:
.用JTA對(duì)事務(wù)進(jìn)行劃分
.事務(wù)劃分代碼被DAO分開
.調(diào)用者承擔(dān)劃分事務(wù)的責(zé)任
.DAO參與一個(gè)全局的事務(wù)中
JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實(shí)現(xiàn)將依賴于你的應(yīng)用程序的特定需求。
日志記錄和DAO
一個(gè)好的DAO實(shí)現(xiàn)類將使用日志記錄來捕獲有關(guān)它在運(yùn)行時(shí)的行為細(xì)節(jié)。你可以選擇記錄異常、配置信息、連接狀態(tài)、JDBC驅(qū)動(dòng)程序的元數(shù)據(jù)或查詢參數(shù)。日志對(duì)開發(fā)整個(gè)階段都是有益的。我經(jīng)常檢查應(yīng)用程序在開發(fā)期間、測試期間和產(chǎn)品中的日志記錄。
在這段中,我們將展現(xiàn)一段如何把Jakarta Commaons Logging結(jié)合中一個(gè)DAO中的例子。在我們開始之前,讓我們先回顧一些基礎(chǔ)知識(shí)。
選擇一個(gè)日志例庫
許多開發(fā)人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個(gè)完整的日志系統(tǒng)的的能力。下表列出了Java平臺(tái)的日志類庫:
日志類庫 開源嗎? URL
Java.util.logging 否 http://java.sun.com/j2ee
Jakarta Log4j 是 http://hajarta.apache.org/log4j/
Jakarta Commons Logging 是 http:/Jakarta.apache.org/commons/logging.html
Java.util.logging是J2SE1.4平臺(tái)上的標(biāo)準(zhǔn)的API。但是,大多數(shù)開發(fā)人員都認(rèn)為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越j(luò)ava.util.logging的優(yōu)點(diǎn)之一就是它支持J2SE1.3和J2SE1.4平臺(tái)。
Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個(gè)把你的應(yīng)用程序獨(dú)立于日志實(shí)現(xiàn)的提取層。使用Commons Logging你能夠通過改變一個(gè)配置文件來與下面的日志實(shí)現(xiàn)來交換數(shù)據(jù)。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一個(gè)日志示例
示例7顯示了在一個(gè)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 ...
}
}
}
日志是評(píng)估應(yīng)用程序的基本部分。如果你在一個(gè)DAO中遇到了失敗,日志經(jīng)常會(huì)為理解發(fā)生的什么錯(cuò)誤提供最好的信息。把日志結(jié)合到你的DAO中,確保得到調(diào)試和解決問題的有效手段。
DAO中的異常處理
我們已經(jīng)看了事務(wù)劃分和日志記錄,并且現(xiàn)在對(duì)于它們是怎樣應(yīng)用于數(shù)據(jù)訪問對(duì)象的有一個(gè)深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護(hù)性。
在實(shí)現(xiàn)DAO模式的時(shí)候,要考濾下面的問題:
.在DAO的public接口中的方法將拋出被檢查的異常嗎?
.如果是,將拋出什么樣的檢查性異常?
.在DAO實(shí)現(xiàn)類中怎能樣處理異常。
在用DAO模式工作的過程中,我們的團(tuán)隊(duì)為異常處理開發(fā)了一組方針。下面的這些方針會(huì)很大程度的改善你的DAO:
.DAO方法應(yīng)該拋出有意義的異常。
.DAO方法不應(yīng)該拋出java.lang.Exception異常。因?yàn)閖ava.lang.Exception太一般化,它不能包含有關(guān)潛在問題的所有信息。
.DAO方法不應(yīng)該拋出java.sql.SQLException異常。SQLException是一個(gè)底層的JDBC異常,DAO應(yīng)用努力封裝JDBC異常而不應(yīng)該把JDBC異常留給應(yīng)用程序的其它部分。
.在DAO接口中的方法應(yīng)該只拋出調(diào)用者期望處理的檢查性異常。如果調(diào)用者不能用適當(dāng)?shù)姆椒▉硖幚懋惓#紴V拋出不檢查性(運(yùn)行時(shí)run-time)異常。
.如果你的數(shù)據(jù)訪問代碼捕獲了一個(gè)異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。
.使用異常鏈把底層的異常傳遞給高層的某個(gè)處理器。
.考濾定義一個(gè)標(biāo)準(zhǔn)的DAO異常類。Spring框架提供了一個(gè)優(yōu)秀的預(yù)定義的DAO異常類的集合。
看Resources,查看有異常和異常處理技術(shù)的更詳細(xì)信息。
實(shí)現(xiàn)示例:MovieDAO
MoveDAO是一個(gè)示范了在這篇文章中所討論的所有技術(shù),包括事務(wù)劃分、日志記錄和異常處理。你會(huì)在Resources段找到MovieDAO的源代碼。它被分下面的三個(gè)包:
.daoexamples.exception
.daoexamples.move
.daoexamples.moviedemo
這個(gè)DAO模式的實(shí)現(xiàn)由下面的類和接口組成:
.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的數(shù)據(jù)操作。這個(gè)接口有如下五個(gè)方法:
.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包包含了兩個(gè)MovieDAO接口的實(shí)現(xiàn)。每個(gè)實(shí)現(xiàn)使用了一個(gè)同的事務(wù)劃分方法,如下表所示:
MovieDAOImpl MovieDAOImplJTA
實(shí)現(xiàn)了MovieDAO接口嗎? Yes Yes
通過JNDI獲得DataSource嗎? Yes Yes
從一個(gè)DataSource獲得java.sql.Connection對(duì)象嗎? Yes Yes
DAO界定內(nèi)部的事務(wù)嗎? Yes No
使用JDBC事務(wù)嗎? Yes No
使用一個(gè)XA DataSource嗎? No Yes
分擔(dān)JTA事務(wù)嗎? No Yes
MovieDAO 示范應(yīng)用程序
這個(gè)示范應(yīng)用程序是一個(gè)叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個(gè)表中的movie數(shù)據(jù)。
這個(gè)servlet示范了把JTA感知的MovieDAO和Java消息服務(wù)組合到一個(gè)單一的事務(wù)中,如示例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();
要運(yùn)行這個(gè)范例應(yīng)用程序,在你的應(yīng)用程序服務(wù)器中配置一個(gè)XA 數(shù)據(jù)源和一個(gè)非XA數(shù)據(jù)源。然后布署daoexamples.ear文件。這個(gè)應(yīng)用程序?qū)⑦\(yùn)行在任何與J2EE兼容的應(yīng)用程序服務(wù)器。
DAO解決了JSP 頁面維護(hù)難的問題,使JSP不能用JAVA.SQL.*所有的數(shù)據(jù)庫代碼用PreapreStatemnet,hirnate,ibait的來做
DAO屬于J2EE數(shù)據(jù)庫的操作,
它封裝了一個(gè)項(xiàng)目的所有的數(shù)據(jù)庫操作
所有的
客戶---VO----DAO
VO對(duì)象和表中的字段完全對(duì)于
查詢?nèi)渴欠祷豅IST,所需要用Iterator來進(jìn)行輸出
import java.util.* ;
import cn.mldn.lxh.vo.* ;
// 規(guī)定出了操作person表在此項(xiàng)目里的全部方法
public interface PersonDAO
{
// 增加操作
public void insert(Person person) throws Exception ;
// 修改操作
public void update(Person person) throws Exception ;
// 刪除操作
public void delete(String id) throws Exception ;
// 按ID查詢操作
public Person queryById(String id) throws Exception ;
// 查詢?nèi)?
public List queryAll() throws Exception ;
// 模糊查詢
public List queryByLike(String cond) throws Exception ;
}
package cn.mldn.lxh.dao.impl ;
import java.sql.* ;
import java.util.* ;
import cn.mldn.lxh.vo.* ;
import cn.mldn.lxh.dbc.* ;
import cn.mldn.lxh.dao.* ;
// 此類需要完成具體的數(shù)據(jù)庫操作,需要JDB代碼
public class PersonDAOImpl implements PersonDAO
{
// 增加操作
public void insert(Person person) throws Exception
{
String sql = "INSERT INTO person (id,name,password,age,email) VALUES (?,?,?,?,?)" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,person.getId()) ;
pstmt.setString(2,person.getName()) ;
pstmt.setString(3,person.getPassword()) ;
pstmt.setInt(4,person.getAge()) ;
pstmt.setString(5,person.getEmail()) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 修改操作
public void update(Person person) throws Exception
{
String sql = "UPDATE person SET name=?,password=?,age=?,email=? WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,person.getName()) ;
pstmt.setString(2,person.getPassword()) ;
pstmt.setInt(3,person.getAge()) ;
pstmt.setString(4,person.getEmail()) ;
pstmt.setString(5,person.getId()) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 刪除操作
public void delete(String id) throws Exception
{
String sql = "DELETE FROM person WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,id) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 按ID查詢操作
public Person queryById(String id) throws Exception
{
Person person = null ;
String sql = "SELECT id,name,password,age,email FROM person WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,id) ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
if(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return person ;
}
// 查詢?nèi)?
public List queryAll() throws Exception
{
List all = new ArrayList() ;
String sql = "SELECT id,name,password,age,email FROM person" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
while(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
Person person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
// 將查詢出來的數(shù)據(jù)加入到List對(duì)象之中
all.add(person) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return all ;
}
// 模糊查詢
public List queryByLike(String cond) throws Exception
{
List all = new ArrayList() ;
String sql = "SELECT id,name,password,age,email FROM person WHERE name LIKE ? or email LIKE ?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
// 設(shè)置模糊查詢條件
pstmt.setString(1,"%"+cond+"%") ;
pstmt.setString(2,"%"+cond+"%") ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
while(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
Person person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
// 將查詢出來的數(shù)據(jù)加入到List對(duì)象之中
all.add(person) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return all ;
}
};
package cn.mldn.lxh.factory ;
import cn.mldn.lxh.dao.* ;
import cn.mldn.lxh.dao.impl.* ;
public class DAOFactory
{
public static PersonDAO getPersonDAOInstance()
{
return new PersonDAOImpl() ;
}
};
J2EE開發(fā)人員使用數(shù)據(jù)訪問對(duì)象(DAO)設(shè)計(jì)模式把底層的數(shù)據(jù)訪問邏輯和高層的商務(wù)邏輯分開.實(shí)現(xiàn)DAO模式能夠更加專注于編寫數(shù)據(jù)訪問代碼.
我們先來回顧一下DAO設(shè)計(jì)模式和數(shù)據(jù)訪問對(duì)象.
DAO基礎(chǔ)
DAO模式是標(biāo)準(zhǔn)的J2EE設(shè)計(jì)模式之一.開發(fā)人員使用這個(gè)模式把底層的數(shù)據(jù)訪問操作和上層的商務(wù)邏輯分開.一個(gè)典型的DAO實(shí)現(xiàn)有下列幾個(gè)組件:
1. 一個(gè)DAO工廠類;
2. 一個(gè)DAO接口;
3. 一個(gè)實(shí)現(xiàn)DAO接口的具體類;
4. 數(shù)據(jù)傳遞對(duì)象(有些時(shí)候叫做值對(duì)象).
具體的DAO類包含了從特定的數(shù)據(jù)源訪問數(shù)據(jù)的邏輯。在下面的這段中你將學(xué)到設(shè)計(jì)和實(shí)現(xiàn)數(shù)據(jù)訪問對(duì)象的技術(shù)。
事務(wù)劃分:
關(guān)于DAO要記住的一件重要事情是它們是事務(wù)性對(duì)象。每個(gè)被DAO執(zhí)行的操作(象創(chuàng)建,更新、或刪除數(shù)據(jù))都是和事務(wù)相關(guān)聯(lián)的。同樣的,事務(wù)劃分(transaction demarcation)的概念是特別重要的。
事務(wù)劃分是在事務(wù)界定定義中的方式。J2EE規(guī)范為事務(wù)劃分描述了兩種模式:編程性事務(wù)(programmatic)和聲明性事務(wù)(declarative).下表是對(duì)這兩種模式的拆分:
聲明性事務(wù)劃分 編程性事務(wù)劃分
程序員使用EJB的布署描述符聲明事務(wù)屬性 程序員擔(dān)負(fù)編寫事務(wù)邏輯代碼的責(zé)任。
運(yùn)行時(shí)環(huán)境(EJB容器)使用這些屬性來自動(dòng)的管理事務(wù)。 應(yīng)用程序通過一個(gè)API接口來控制事務(wù)。
我將把注意力集中的編程性事務(wù)劃分上。
象前面的介紹一樣,DAOs是一些事務(wù)對(duì)象。一個(gè)典型的DAO要執(zhí)行象創(chuàng)建、更新、和刪除這的事務(wù)性操作。在設(shè)計(jì)一個(gè)DAO時(shí),首先要問自己如下問題:
1、 事務(wù)將怎樣開始?
2、 事務(wù)將怎樣結(jié)束?
3、 那個(gè)對(duì)象將承擔(dān)起動(dòng)一個(gè)事務(wù)的責(zé)任?
4、 那個(gè)對(duì)象將承擔(dān)結(jié)束一個(gè)事務(wù)的責(zé)任?
5、 DAO應(yīng)該承擔(dān)起動(dòng)和結(jié)束事務(wù)的責(zé)任?
6、 應(yīng)用程序需要交叉訪問多個(gè)DAO嗎?
7、 一個(gè)事務(wù)包含一個(gè)DAO還是多個(gè)DAO?
8、 一個(gè)DAO包含其它的DAO中的方法嗎?
回答這些問題將有助于你為DAO對(duì)象選擇最好的事務(wù)劃分策略。對(duì)ADO中的事務(wù)劃分有兩個(gè)主要的策略。一種方法是使用DAO承擔(dān)事務(wù)劃分的責(zé)任;另一種是延期性事務(wù),它把事務(wù)劃分到調(diào)用DAO對(duì)象的方法中。如果你選擇前者,你將要在DAO類中嵌入事務(wù)代碼。如果你選擇后者,事務(wù)代碼將被寫在DAO類的外部。我們將使用簡單的代碼實(shí)例來更好的理解這兩種方法是怎樣工作的。
實(shí)例1展示了一個(gè)帶有兩種數(shù)據(jù)操作的DAO:創(chuàng)建(create)和更新(update):
public void createWarehouseProfile(WHProfile profile);
public void updateWarehouseStatus(WHIdentifier id, StatusInfo status);
實(shí)例2展示了一個(gè)簡單的事務(wù),事務(wù)劃分代碼是在DAO類的外部。注意:在這個(gè)例子中的調(diào)用者把多個(gè)DOA操作組合到這個(gè)事務(wù)中。
tx.begin(); // start the transaction
dao.createWarehouseProfile(profile);
dao.updateWarehouseStatus(id1, status1);
dao.updateWarehouseStatus(id2, status2);
tx.commit(); // end the transaction
這種事務(wù)事務(wù)劃分策略對(duì)在一個(gè)單一事務(wù)中訪問多個(gè)DAO的應(yīng)用程序來說尤為重要。
你即可使用JDBC API也可以使用Java 事務(wù)API(JTA)來實(shí)現(xiàn)事務(wù)的劃分。JDBC事務(wù)劃分比JTA事務(wù)劃分簡單,但是JTA提供了更好的靈活性。在下面的這段中,我們會(huì)進(jìn)一步的看事務(wù)劃分機(jī)制。
使用JDBC的事務(wù)劃分
JDBC事務(wù)是使用Connection對(duì)象來控制的。JDBC的連接接口(java.sql.Connection)提供了兩種事務(wù)模式:自動(dòng)提交和手動(dòng)提交。Java.sql.Connection為控制事務(wù)提供了下列方法:
.public void setAutoCommit(Boolean)
.public Boolean getAutoCommit()
.public void commit()
.public void rollback()
實(shí)例3展示怎樣使用JDBC API來劃分事務(wù):
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事務(wù)劃分,你能夠把多個(gè)SQL語句組合到一個(gè)單一事務(wù)中。JDBC事務(wù)的缺點(diǎn)之一就是事務(wù)范圍被限定在一個(gè)單一的數(shù)據(jù)庫連接中。一個(gè)JDBC事務(wù)不能夠跨越多個(gè)數(shù)據(jù)庫。接下來,我們會(huì)看到怎樣使用JTA來做事務(wù)劃分的。因?yàn)镴TA不象JDBC那樣被廣泛的了解,所以我首先概要的介紹一下JTA。
JTA概要介紹
Java事務(wù)API(JTA Java Transaction API)和它的同胞Java事務(wù)服務(wù)(JTS Java Transaction Service),為J2EE平臺(tái)提供了分布式事務(wù)服務(wù)。一個(gè)分布式事務(wù)(distributed transaction)包括一個(gè)事務(wù)管理器(transaction manager)和一個(gè)或多個(gè)資源管理器(resource manager)。一個(gè)資源管理器(resource manager)是任意類型的持久化數(shù)據(jù)存儲(chǔ)。事務(wù)管理器(transaction manager)承擔(dān)著所有事務(wù)參與單元者的相互通訊的責(zé)任。下車站顯示了事務(wù)管理器和資源管理的間的關(guān)系。
JTA事務(wù)比JDBC事務(wù)更強(qiáng)大。一個(gè)JTA事務(wù)可以有多個(gè)參與者,而一個(gè)JDBC事務(wù)則被限定在一個(gè)單一的數(shù)據(jù)庫連接。下列任一個(gè)Java平臺(tái)的組件都可以參與到一個(gè)JTA事務(wù)中:
.JDBC連接
.JDO PersistenceManager 對(duì)象
.JMS 隊(duì)列
.JMS 主題
.企業(yè)JavaBeans(EJB)
.一個(gè)用J2EE Connector Architecture 規(guī)范編譯的資源分配器。
使用JTA的事務(wù)劃分
要用JTA來劃分一個(gè)事務(wù),應(yīng)用程序調(diào)用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個(gè)典型的JNDI搜索的UseTransaction對(duì)象。
import javax.transaction.*;
import javax.naming.*;
// ...
InitialContext ctx = new InitialContext();
Object txObj = ctx.lookup("java:comp/UserTransaction");
UserTransaction utx = (UserTransaction) txObj;
應(yīng)用程序有了UserTransaction對(duì)象的引用之后,就可以象示例5那樣來起動(dòng)事務(wù)。
utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement("UPDATE MOVIES ...");
pstmt.setString(1, "Spinal Tap");
pstmt.executeUpdate();
// ...
utx.commit();
// ...
當(dāng)應(yīng)用程序調(diào)用commit()時(shí),事務(wù)管理器使用兩段提交協(xié)議來結(jié)束事務(wù)。
JTA事務(wù)控制的方法
.javax.transaction.UserTransaction接口提供了下列事務(wù)控制方法:
.public void begin()
.public void commit()
.public void rollback()
.public void getStatus()
.public void setRollbackOnly()
.public void setTransactionTimeout(int)
應(yīng)用程序調(diào)用begin()來起動(dòng)事務(wù),即可調(diào)用commit()也可以調(diào)用rollback()來結(jié)束事務(wù)。
使用JTA和JDBC
開發(fā)人員經(jīng)常使用JDBC來作為DAO類中的底層數(shù)據(jù)操作。如果計(jì)劃使用JTA來劃分事務(wù),你將需要一個(gè)實(shí)現(xiàn)了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅(qū)動(dòng)。實(shí)現(xiàn)了這些接口的驅(qū)動(dòng)將有能力參與到JTA事務(wù)中。一個(gè)XADataSource對(duì)象是一個(gè)XAConnection對(duì)象的工廠。XAConnections是參與到JTA事務(wù)中的連接。
你需要使用應(yīng)用程序服務(wù)器管理工具來建立XADataSource對(duì)象。對(duì)于特殊的指令請(qǐng)參考應(yīng)用程序服務(wù)器文檔和JDBC驅(qū)動(dòng)文檔。
J2EE應(yīng)用程序使用JNDI來查找數(shù)據(jù)源。一旦應(yīng)用程序有了一個(gè)數(shù)據(jù)源對(duì)象的引用,這會(huì)調(diào)用javax.sql.DataSource.getConnection()來獲得數(shù)據(jù)庫的連接。
XA連接區(qū)別于非XA連接。要記住的是XA連接是一個(gè)JTA事務(wù)中的參與者。這就意味著XA連接不支持JDBC的自動(dòng)提交特性。也就是說應(yīng)用程序不必在XA連接上調(diào)用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應(yīng)用程序應(yīng)該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().
選擇最好的方法
我們已經(jīng)討論了JDBC和JTA是怎樣劃分事務(wù)的。每一種方法都有它的優(yōu)點(diǎn),回此你需要決定為你的應(yīng)用程序選擇一個(gè)最適應(yīng)的方法。
在我們團(tuán)隊(duì)許多最近的對(duì)于事務(wù)劃分的項(xiàng)目中使用JDBC API來創(chuàng)建DAO類。這DAO類總結(jié)如下:
.事務(wù)劃分代碼被嵌入到DAO類內(nèi)部
.DAO類使用JDBC API來進(jìn)行事務(wù)劃分
.調(diào)用者沒有劃分事務(wù)的方法
.事務(wù)范圍被限定在一個(gè)單一的JDBC連接
JDBC事務(wù)對(duì)復(fù)雜的企業(yè)應(yīng)用程序不總是有效的。如果你的事務(wù)將跨越多個(gè)DAO對(duì)象或
多個(gè)數(shù)據(jù)庫,那么下面的實(shí)現(xiàn)策略可能會(huì)更恰當(dāng)。:
.用JTA對(duì)事務(wù)進(jìn)行劃分
.事務(wù)劃分代碼被DAO分開
.調(diào)用者承擔(dān)劃分事務(wù)的責(zé)任
.DAO參與一個(gè)全局的事務(wù)中
JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實(shí)現(xiàn)將依賴于你的應(yīng)用程序的特定需求。
日志記錄和DAO
一個(gè)好的DAO實(shí)現(xiàn)類將使用日志記錄來捕獲有關(guān)它在運(yùn)行時(shí)的行為細(xì)節(jié)。你可以選擇記錄異常、配置信息、連接狀態(tài)、JDBC驅(qū)動(dòng)程序的元數(shù)據(jù)或查詢參數(shù)。日志對(duì)開發(fā)整個(gè)階段都是有益的。我經(jīng)常檢查應(yīng)用程序在開發(fā)期間、測試期間和產(chǎn)品中的日志記錄。
在這段中,我們將展現(xiàn)一段如何把Jakarta Commaons Logging結(jié)合中一個(gè)DAO中的例子。在我們開始之前,讓我們先回顧一些基礎(chǔ)知識(shí)。
選擇一個(gè)日志例庫
許多開發(fā)人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個(gè)完整的日志系統(tǒng)的的能力。下表列出了Java平臺(tái)的日志類庫:
日志類庫 開源嗎? URL
Java.util.logging 否 http://java.sun.com/j2ee
Jakarta Log4j 是 http://hajarta.apache.org/log4j/
Jakarta Commons Logging 是 http:/Jakarta.apache.org/commons/logging.html
Java.util.logging是J2SE1.4平臺(tái)上的標(biāo)準(zhǔn)的API。但是,大多數(shù)開發(fā)人員都認(rèn)為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越j(luò)ava.util.logging的優(yōu)點(diǎn)之一就是它支持J2SE1.3和J2SE1.4平臺(tái)。
Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個(gè)把你的應(yīng)用程序獨(dú)立于日志實(shí)現(xiàn)的提取層。使用Commons Logging你能夠通過改變一個(gè)配置文件來與下面的日志實(shí)現(xiàn)來交換數(shù)據(jù)。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一個(gè)日志示例
示例7顯示了在一個(gè)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 ...
}
}
}
日志是評(píng)估應(yīng)用程序的基本部分。如果你在一個(gè)DAO中遇到了失敗,日志經(jīng)常會(huì)為理解發(fā)生的什么錯(cuò)誤提供最好的信息。把日志結(jié)合到你的DAO中,確保得到調(diào)試和解決問題的有效手段。
DAO中的異常處理
我們已經(jīng)看了事務(wù)劃分和日志記錄,并且現(xiàn)在對(duì)于它們是怎樣應(yīng)用于數(shù)據(jù)訪問對(duì)象的有一個(gè)深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護(hù)性。
在實(shí)現(xiàn)DAO模式的時(shí)候,要考濾下面的問題:
.在DAO的public接口中的方法將拋出被檢查的異常嗎?
.如果是,將拋出什么樣的檢查性異常?
.在DAO實(shí)現(xiàn)類中怎能樣處理異常。
在用DAO模式工作的過程中,我們的團(tuán)隊(duì)為異常處理開發(fā)了一組方針。下面的這些方針會(huì)很大程度的改善你的DAO:
.DAO方法應(yīng)該拋出有意義的異常。
.DAO方法不應(yīng)該拋出java.lang.Exception異常。因?yàn)閖ava.lang.Exception太一般化,它不能包含有關(guān)潛在問題的所有信息。
.DAO方法不應(yīng)該拋出java.sql.SQLException異常。SQLException是一個(gè)底層的JDBC異常,DAO應(yīng)用努力封裝JDBC異常而不應(yīng)該把JDBC異常留給應(yīng)用程序的其它部分。
.在DAO接口中的方法應(yīng)該只拋出調(diào)用者期望處理的檢查性異常。如果調(diào)用者不能用適當(dāng)?shù)姆椒▉硖幚懋惓#紴V拋出不檢查性(運(yùn)行時(shí)run-time)異常。
.如果你的數(shù)據(jù)訪問代碼捕獲了一個(gè)異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。
.使用異常鏈把底層的異常傳遞給高層的某個(gè)處理器。
.考濾定義一個(gè)標(biāo)準(zhǔn)的DAO異常類。Spring框架提供了一個(gè)優(yōu)秀的預(yù)定義的DAO異常類的集合。
看Resources,查看有異常和異常處理技術(shù)的更詳細(xì)信息。
實(shí)現(xiàn)示例:MovieDAO
MoveDAO是一個(gè)示范了在這篇文章中所討論的所有技術(shù),包括事務(wù)劃分、日志記錄和異常處理。你會(huì)在Resources段找到MovieDAO的源代碼。它被分下面的三個(gè)包:
.daoexamples.exception
.daoexamples.move
.daoexamples.moviedemo
這個(gè)DAO模式的實(shí)現(xiàn)由下面的類和接口組成:
.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的數(shù)據(jù)操作。這個(gè)接口有如下五個(gè)方法:
.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包包含了兩個(gè)MovieDAO接口的實(shí)現(xiàn)。每個(gè)實(shí)現(xiàn)使用了一個(gè)同的事務(wù)劃分方法,如下表所示:
MovieDAOImpl MovieDAOImplJTA
實(shí)現(xiàn)了MovieDAO接口嗎? Yes Yes
通過JNDI獲得DataSource嗎? Yes Yes
從一個(gè)DataSource獲得java.sql.Connection對(duì)象嗎? Yes Yes
DAO界定內(nèi)部的事務(wù)嗎? Yes No
使用JDBC事務(wù)嗎? Yes No
使用一個(gè)XA DataSource嗎? No Yes
分擔(dān)JTA事務(wù)嗎? No Yes
MovieDAO 示范應(yīng)用程序
這個(gè)示范應(yīng)用程序是一個(gè)叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個(gè)表中的movie數(shù)據(jù)。
這個(gè)servlet示范了把JTA感知的MovieDAO和Java消息服務(wù)組合到一個(gè)單一的事務(wù)中,如示例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();
要運(yùn)行這個(gè)范例應(yīng)用程序,在你的應(yīng)用程序服務(wù)器中配置一個(gè)XA 數(shù)據(jù)源和一個(gè)非XA數(shù)據(jù)源。然后布署daoexamples.ear文件。這個(gè)應(yīng)用程序?qū)⑦\(yùn)行在任何與J2EE兼容的應(yīng)用程序服務(wù)器。
DAO解決了JSP 頁面維護(hù)難的問題,使JSP不能用JAVA.SQL.*所有的數(shù)據(jù)庫代碼用PreapreStatemnet,hirnate,ibait的來做
DAO屬于J2EE數(shù)據(jù)庫的操作,
它封裝了一個(gè)項(xiàng)目的所有的數(shù)據(jù)庫操作
所有的
客戶---VO----DAO
VO對(duì)象和表中的字段完全對(duì)于
查詢?nèi)渴欠祷豅IST,所需要用Iterator來進(jìn)行輸出
import java.util.* ;
import cn.mldn.lxh.vo.* ;
// 規(guī)定出了操作person表在此項(xiàng)目里的全部方法
public interface PersonDAO
{
// 增加操作
public void insert(Person person) throws Exception ;
// 修改操作
public void update(Person person) throws Exception ;
// 刪除操作
public void delete(String id) throws Exception ;
// 按ID查詢操作
public Person queryById(String id) throws Exception ;
// 查詢?nèi)?
public List queryAll() throws Exception ;
// 模糊查詢
public List queryByLike(String cond) throws Exception ;
}
package cn.mldn.lxh.dao.impl ;
import java.sql.* ;
import java.util.* ;
import cn.mldn.lxh.vo.* ;
import cn.mldn.lxh.dbc.* ;
import cn.mldn.lxh.dao.* ;
// 此類需要完成具體的數(shù)據(jù)庫操作,需要JDB代碼
public class PersonDAOImpl implements PersonDAO
{
// 增加操作
public void insert(Person person) throws Exception
{
String sql = "INSERT INTO person (id,name,password,age,email) VALUES (?,?,?,?,?)" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,person.getId()) ;
pstmt.setString(2,person.getName()) ;
pstmt.setString(3,person.getPassword()) ;
pstmt.setInt(4,person.getAge()) ;
pstmt.setString(5,person.getEmail()) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 修改操作
public void update(Person person) throws Exception
{
String sql = "UPDATE person SET name=?,password=?,age=?,email=? WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,person.getName()) ;
pstmt.setString(2,person.getPassword()) ;
pstmt.setInt(3,person.getAge()) ;
pstmt.setString(4,person.getEmail()) ;
pstmt.setString(5,person.getId()) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 刪除操作
public void delete(String id) throws Exception
{
String sql = "DELETE FROM person WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,id) ;
// 進(jìn)行數(shù)據(jù)庫更新操作
pstmt.executeUpdate() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
}
// 按ID查詢操作
public Person queryById(String id) throws Exception
{
Person person = null ;
String sql = "SELECT id,name,password,age,email FROM person WHERE id=?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
pstmt.setString(1,id) ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
if(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return person ;
}
// 查詢?nèi)?
public List queryAll() throws Exception
{
List all = new ArrayList() ;
String sql = "SELECT id,name,password,age,email FROM person" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
while(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
Person person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
// 將查詢出來的數(shù)據(jù)加入到List對(duì)象之中
all.add(person) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return all ;
}
// 模糊查詢
public List queryByLike(String cond) throws Exception
{
List all = new ArrayList() ;
String sql = "SELECT id,name,password,age,email FROM person WHERE name LIKE ? or email LIKE ?" ;
PreparedStatement pstmt = null ;
DataBaseConnection dbc = null ;
// 下面是針對(duì)數(shù)據(jù)庫的具體操作
try
{
// 連接數(shù)據(jù)庫
dbc = new DataBaseConnection() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;
// 設(shè)置模糊查詢條件
pstmt.setString(1,"%"+cond+"%") ;
pstmt.setString(2,"%"+cond+"%") ;
// 進(jìn)行數(shù)據(jù)庫查詢操作
ResultSet rs = pstmt.executeQuery() ;
while(rs.next())
{
// 查詢出內(nèi)容,之后將查詢出的內(nèi)容賦值給person對(duì)象
Person person = new Person() ;
person.setId(rs.getString(1)) ;
person.setName(rs.getString(2)) ;
person.setPassword(rs.getString(3)) ;
person.setAge(rs.getInt(4)) ;
person.setEmail(rs.getString(5)) ;
// 將查詢出來的數(shù)據(jù)加入到List對(duì)象之中
all.add(person) ;
}
rs.close() ;
pstmt.close() ;
}
catch (Exception e)
{
throw new Exception("操作出現(xiàn)異常") ;
}
finally
{
// 關(guān)閉數(shù)據(jù)庫連接
dbc.close() ;
}
return all ;
}
};
package cn.mldn.lxh.factory ;
import cn.mldn.lxh.dao.* ;
import cn.mldn.lxh.dao.impl.* ;
public class DAOFactory
{
public static PersonDAO getPersonDAOInstance()
{
return new PersonDAOImpl() ;
}
};