作者:Sunil Patil
譯者:rotter_pal
版權聲明:任何獲得Matrix授權的網站,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
作者:Sunil Patil;
rotter_pal原文地址:
http://www.onjava.com/pub/a/onjava/2005/08/10/ibatisdao.html中文地址:
http://www.matrix.org.cn/resource/article/44/44058_iBatis+DAO.html關鍵詞: iBatis DAO
在核心J2EE模式中是這樣介紹DAO模式的:為了建立一個健壯的J2EE應用,應該將所有對數據源的訪問操作抽象封裝在一個公共API中。用程序設計的語言來說,就是建立一個接口,接口中定義了此應用程序中將會用到的所有事務方法。在這個應用程序中,當需要和數據源進行交互的時候則使用這個接口,并且編寫一個單獨的類來實現這個接口在邏輯上對應這個特定的數據存儲。
比如考慮在iBatis: SQL Maps中的應用例子。這是一個Struts應用允許對一個關系表執行SELECT, INSERT, UPDATE和DELETE的SQL請求。在這個應用中,使用SQL Maps做持續性框架。現在我們要修改這個應用,將這個關系表儲存在一個XML文件中而不是存在關系數據庫中,或者使用Hibernate來實現SELECT請求,而用SQL Map來執行其他請求,因為Hibernate提供了對高速緩存更好的支持。這樣的修改很難實現,或者即使我們能修改而實現了這個功能,也會是很混亂的解決方案。
對于這類問題更好的解決方法是建立一個ContactDAO接口,在這個接口中定義處理SELECT, INSERT, UPDATE, 和DELETE 請求的事務方法。然后根據不同的事務邏輯建立不同的類實現各個方法。所以可能會有一個類處理使用SQL Maps同關系表進行交互的情況,而另外一個類處理用XML文件存放關系表而不是關系數據庫的情況,等等。在項目中,根據實際的需要從不同的ContactDAO中選擇相應的實現。這種關系見圖1:

圖1. ContactDAO 接口及實現
iBatis DAO是由Apache主持的開源框架項目,主要目標是為了解決這類問題。它允許在工程中以DAO模式為基礎建立應用。這就意味著可以建立一個XML文件,并聲明XMLContactDAO.java是ContactDAO的實現類,這個類知道如何從XML文件中讀寫數據。SQLMapContactDAO則知道如何用SQL Maps作為持續化框架與關系表進行交互。在工程中,如果向DAO框架提交一個需要XML的ContactDAO請求,框架則會返回一個XMLContactDAO對象。同樣的DAO框架提供了唯一的接口處理事務管理,這個接口能實現與數據的存儲方式無關。它同樣考慮了底層連接管理細節和初始化存儲框架。
這篇文章是關于如何一步一步的在項目中應用iBatis DAO框架的基礎指導。我們將由如何把SQL Maps一文中的應用實例改為應用DAO框架入手。然后,我們要討論DAO框架的構造。再下一步,我們關注事務管理是如何在DAO框架中得到支持的。最后一部分是關于如何建立自己的事務管理模塊。
示例應用首先,我們將SQL Maps一文中的例子改為應用DAO框架。
1.????????將ibatis-dao-2.jar文件復制到WEB-INF/lib目錄下。
2.????????在Java源程序的目錄里新建一個如下的DAOMap.xml文件
清單1:<daoConfig>
<context id="sqlmap">
??<transactionManager type="SQLMAP">
??<property name="SqlMapConfigResource" value=
?? "com/sample/contact/dao/sqlmap/SqlMapConfig.xml"/>
??</transactionManager>
??<dao interface="com.sample.contact.dao.ContactDAO"
?? implementation=
?? "com.sample.contact.dao.sqlmap.SQLMapContactDAO"/>
</context>
</daoConfig>
DAOMap.xml是發布iBatis DAO框架的配置文件。<daoConfig>是根元素,每個<context>元素描述了一種存儲機制。在這個例子中只使用了SQL Maps來存儲,所以我們這里只有一個<context>元素。每種存儲機制必須包含一個<transactionManager>元素,這個元素描述連接它后面的數據存儲所用的管理器,并且標記事務的界限。我們將在稍后再討論transactionManager。
<context>元素還包括一組DAO用于描述其他的存儲管理機制。在這個例子中,我們將生成一個使用SQL Maps存儲的ContactDAO,所以在配置文件中添加一個ie<dao>標記來定義SQLMapContactDAO。
3.????????建立ContactDAO.java,如下:
清單2:public interface ContactDAO extends DAO {
????public int insertContact(Contact contact);
????public int updateContact(Contact contact);
????public Contact selectContact(int contactId);
????public int deleteContact(int contactId);
}
??
ContactDAO.java定義了用戶和一個關系表進行交互所需要用到的所有事務處理方法。請注意到ContactDAO.java中的所有方法都將一個Contact對象作為參數,這是一個用來攜帶數據的數據傳遞對象。
4.????????建立一個SQLMapContactDAO.java文件,如下
清單3:public class SQLMapContactDAO extends
SqlMapDaoTemplate implements ContactDAO {
??public SQLMapContactDAO(DaoManager arg0) {
??????super(arg0);
??}
??public int deleteContact(int contactId) {
????return super.delete("deleteContact",
????new Integer(contactId));
??}
??public int insertContact(Contact contact) {
????Integer contactId =(Integer)super.insert
??????("insertContact",contact);
????return contact.getContactId();
??}
??public Contact selectContact(int contactId) {
????return (Contact)super.queryForObject("getContact",
??????new Integer(contactId));
??}
??public int updateContact(Contact contact) {
????return super.update("updateContact",contact);
??}
}
SQLMapContactDAO是ContactDAO接口的具體實現,它用SQL Maps作為存儲管理機制。注意到我們并沒有寫任何代碼來或者初始化SQL Maps,或得到一個連接,或者在類中標注一個事務的界限。相反,我們繼承SqlMapDaoTemplate.java類,它幫我們處理下層的、反復的操作。我們在SQLMapContactDAO類中需要考慮的唯一的事情就是事務處理邏輯。
5.????????修改ContactSelectAction.java類中的execute()方法,如下:
清單4:Contact contactForm = (Contact) form;
Reader reader=
??Resources.getResourceAsReader("DAOMap.xml");
DaoManager daoManager =
??DaoManagerBuilder.buildDaoManager(reader);
ContactDAO contactDAO =
??(ContactDAO) daoManager.getDao(
ContactDAO.class,"sqlmap");
request.setAttribute("contactDetail",
??contactDAO.selectContact(
????contactForm.getContactId()));
最后一步是修改ContactSelectAction類中的execute()方法,使它使用DAO框架。為了初始化DAO框架,我們需要一個為DAOMap.xml 準備一個Reader對象。iBatis框架為我們提供了方法Resources.getResourceAsReader()來讀取資源。一旦有了Reader對象來讀取DAOMap.xml,就能將它們讀取至DAOManagerBuilder.buildDaoManager(),返回一個DaoManager實例,將來用于與DAO框架進行交互。從理論上來說,應該在項目啟動的時候初始化DAO框架,在我們這個程序中,可以將這個模塊放入Struts插件中,但是為了簡化這個例子,我們將初始化模塊放入execute方法中。
有了DaoManager實例后,可以調用相應的接口和存儲實現類(在<context>元素中的id屬性值)的getDao()方法。在我們的例子中,需要一個SQLMapContactDAO的實例,所以以ContactDAO為接口名稱,“sqlmap”為存儲機制。一旦實現了SQLMapContactDAO實例,就可以在調用其中的事務方法。
在最后的資源章節中可以下載到這個例子的源碼。
DAO框架架構由于有了一個可以運行的示例,讓我們得以粗略了解DAO框架是如何運作的。在圖2表示的順序圖中演示了DAO的工作方式:

圖2. DAO順序圖
在開始時,調用DaoManagerBuilder.buildDaoManager()并傳入DAOMap.xml來初始化DAO框架。在這個方法中DAO框架會讀取DAOMap.xml并且由此生成相應的DAOManager對象。這個對象包括了對支持的數據存儲機制的描述。哪個接口會被實現,哪個是接口和存儲機制結合的實現類?基本上這是和DAOMap.xml文件相等的 Java對象。
當有了DAOManager對象,可以從中得到ContactDAO接口的SQL Map實例。DAO框架會返回一個包裝了實現類的DaoProxy對象。在本例子中將給SQLMapContactDAO返回一個DaoProxy對象。這個DaoProxy對象允許DAO框架截獲調用商業方法。本例中,當調用 contactDAO.selectContact()時,DAO框架會截獲這個調用并檢查事務處理是否已經開始執行,如果沒有,它將調用事務管理器中的startTransaction()創建一個新的事務處理調用。如果處理已經開始,DaoProxy對象會調用事務中的SQLMapContactDAO中的selectContact()方法。當selectContact()調用返回的時候,DaoProxy對象截獲返回并提交給事務。
如果不希望事務在方法層上可見,或者希望在一個事務中調用多個不同的方法,則可在調用ContactDAO中的商業方法前調用daoManager.startTransaction(),然后在daoManager.startTransaction()執行完以后再提交商業方法。
那么現在剩下要關心的事情就是那個模塊負責存儲機制的初始化并傳遞控制給存儲機制。在這個例子中,就意味著由哪個模塊負責將SqlMapConfig.xml的路徑傳遞給SQL Map框架并給它初始化。同樣意味著哪個模塊負責和SQL Maps框架進行實際的交互。DAO框架為每種存儲提供了Template類,在工程中,可以從這個Template類中繼承實例類,并只要自己的方法中編寫商業事務邏輯。然后將控制傳遞給這個模板類,它將負責和存儲機制的交互。在我們的例子中調用super.queryForObject("getContact",new Integer(contactId)),意味著SqlMapDaoTemplate將負責SQL Maps的初始化和與之交互。
初始化存儲機制需要相關的一些信息,在例子中初始化需要SqlMapConfig.xml的路徑,這個文件中包含驅動類的名字、JDBC URL、登陸信息之類的信息。這些特定的事務管理器需要的信息將會在DaoMap.xml文件中作為一個屬性元素傳遞給管理器。下一節,我們將討論DAO框架支持哪些事務管理器,每個管理器需要哪些初始化信息。
支持的存儲管理機制DAO框架提供了內置的對一些存儲管理機制的支持。為了使用其中的一個內置的transactionManagers,需要做兩件事情:
1.在DAOMap.xml中增加一個<transactionManager>元素來聲明對存儲管理機制的支持。
2.在生成DAO實現類的時候為transactionManager繼承適當的Template類。
下面我們要研究內置transactionManagers并找出在應用程序中使用如何使用它們。
JDBC如果不想使用任何存儲框架,不想自己寫JDBC代碼,那么JDBC事務管理器是很好的選擇。如果使用JDBC作為存儲機制,則可以使用以下三種連接管理之一:
SIMPLE:如果要使用iBatis'自己的連接池實例,可以把SIMPLE作為DataSource元素的值。將通常的JDBC屬性(DriverManager類, JDBC URL,等等)傳入作為Properties。在iBatis在線文檔中查看更多的連接屬性。
清單5:<transactionManager type="JDBC">
<property name="DataSource" value="SIMPLE"/>
<property name="JDBC.Driver"
??value="com.ibm.db2j.jdbc.DB2jDriver"/>
<property name="JDBC.ConnectionURL"
??value="jdbc:db2j:D:\cloudscape\wpsdb"/>
<property name="JDBC.Username"
??value="db2admin"/>
<property name="JDBC.Password"
??value="db2admin"/>
<property name="JDBC.DefaultAutoCommit"
??value="true" />
</transactionManager>
DBCP:使用Apache DBCP作為連接管理。請查看DAO在線指導獲得如何配置DBCP連接池的信息。
JNDI:當要使用應用服務器的連接池,那么要做的是提供連接池的JNDI名,DAO框架則使用這個名稱獲得一個連接。
清單6:<transactionManager type="JDBC">
<property name="DataSource" value="JNDI"/>
<property name="DBJndiContext"
??value="java:comp/env/jdbc/MyDataSource"/>
</transactionManager>
然后要建立一個類繼承JdbcDaoTemplate.java來實現事務方法借口。在示例中,我們建立了JDBCContactDAO.java。在事務方法中,可以調用getConnection()向父類請求連接。因為我們沒有使用任何存儲框架,所以我們只能建立并執行我們自己的SQL請求。
清單7:public int updateContact(Contact contact) {
try {
??Connection conn = getConnection();
??PreparedStatement updateStmt =
?? conn.prepareStatement("UPDATE DB2ADMIN.CONTACT
????SET FIRSTNAME=?,LASTNAME=? WHERE CONTACTID=?");
??updateStmt.setString(1, contact.getFirstName());
??updateStmt.setString(2, contact.getLastName());
??updateStmt.setInt(3, contact.getContactId());
??return updateStmt.executeUpdate();
} catch (SQLException ex) {
????throw new DaoException(ex);
}
}
使用JDBC transactionManager的時候,DAO框架會調用Connection 對象中的commit和rollback方法來控制事務處理。所以事務會在Connection層被處理,而不參與全局事務處理。
JTA如果項目是J2EE應用,那么使用應用服務器提供的連接池會更有利,因為它將比SIMPLE 或者DBCP 連接池有更好的性能。同樣的,使用J2EE應用,RDBMS是唯一的處理源,除了RDBMS還需要包含JCA、MQ Server等功能。因為不能在連接層開始和處理事務,而要特別的在全局事務處理時在一個UserTransaction對象中調用begin()和commit()方法。所以對于這類請求,可以使用JTA 作為transctionManager,既可以向JNDI URL提供數據源連接池,也可以在里面包含UserTransaction對象。
清單8:<transactionManager type="JTA">
<property name="DBJndiContext"
??value="java:comp/env/jdbc/MyDataSource"/>
<property name="UserTransaction"
??value="java:comp/env/UserTransaction"/>
</transactionManager>
Hibernate因為Hibernate是很常見的存儲框架,iBatis DAO也提供了對它的支持。為了在項目中使用Hibernate,像下面那樣在DAOMap.xml增加<transactionManager>元素:
清單9:<transactionManager type="HIBERNATE">
<property name="hibernate.dialect"
??value="net.sf.hibernate.dialect.Cloudscape"/>
<property name="hibernate.connection.driver_class"
??value="com.ibm.db2j.jdbc.DB2jDriver"/>
<property name="hibernate.connection.url"
??value="jdbc:db2j:D:\cloudscape\wpsdb"/>
<property name="hibernate.connection.username"
??value="db2admin/>
<property name="hibernate.connection.password"
??value="db2admin"/>
<property name="class.1"
??value="com.sample.contact.Contact"/>
</transactionManager>
同樣的,需要建立一個DAO類繼承HibernateDaoTemplate。在這個DAO內,可以通過調用getSession()方法來獲得Hibernate Session對象的入口。
SQL MAP請查看示例(在資源小節中)了解如何在項目中使用SQL Map存儲框架的細節。
外部管理外部的事務管理器允許事務處理在外部被DAO框架控制。這種行為有利于處理和非關系數據庫數據源的交互。下一節,我們將討論如何用DAO框架處理以XML文件作為數據源的情況。
部署xml事務Map你可能也經常遇到這種情況:需要從xml中讀取數據,而不是從RDBMS中讀取,假想你正在從事一個銀行項目,你并不能夠直接接觸到銀行的數據庫,所有的用戶信息暫時都會通過一個XML文件傳輸給你,你必須使用這個XML文件進行開發,開發完畢再部署到真正的使用RDBMS的環境中,
這樣的話,你需要做一下改變:
1. 在DAOMap.xml 中增加對外部的transactionManager 的支持。
2. 新建一個XMLContactDAO.java文件:
清單10:public class XMLContactDAO implements ContactDAO {
public static final String
??CONTACTXMLNAME = "c:\\Contact.xml";
public XMLContactDAO(DaoManager manager) {
????super(manager);
}
public int insertContact(Contact contact) {
??HashMap contactMap = loadChanges();
??if (contactMap.get(new Integer
????(contact.getContactId())) == null)
?? contactMap.put(new
????Integer(contact.getContactId()), contact);
??saveChanges(contactMap);
??return contact.getContactId();
}
public Contact selectContact(int contactId) {
??HashMap contactMap = loadChanges();
??return (Contact) contactMap.get(
?? new Integer(contactId));
}
public HashMap loadChanges() {
??HashMap contactMap = null;
??try {
?? XStream xstream = new XStream(new DomDriver());
?? xstream.alias("contact", Contact.class);
?? contactMap =
????(HashMap) xstream.fromXML(
???? new FileReader(CONTACTXMLNAME),HashMap.class);
??} catch (FileNotFoundException e) {
????e.printStackTrace();
????return new HashMap();
??}
??return contactMap;
}
public void saveChanges(HashMap contactMap) {
??try {
?? XStream xstream = new XStream();
?? xstream.alias("contact", Contact.class);
?? xstream.toXML(contactMap,
????new FileWriter(CONTACTXMLNAME));
?? } catch (IOException e) {
?? e.printStackTrace();
??}
}
}
這個例子中,XMLContactDAO實現了ContactDAO事務接口。因為我們使用了一個EXTERNAL事務管理器,所以不能使用任何已經存在的Template類。在我們的類中,我們使用XStream框架新建了兩個簡單的方法——loadChanges()和saveChanges()——實現對XML文件的讀寫。XStream是一個開源框架,實現將一個XML文件看作一個對象來讀取,將對象保存為XML文件的功能。
結論當今,有很多新的存儲框架出現。這對于一個程序員既有好處也有壞處。好處是有更多的選擇余地。壞處是因為你必須作出一個選擇,更糟糕的是不得不在項目開始的時候就選擇一種框架,這就意味著你可能不能完全清楚的了解項目的需求,或者不能完全確信這種框架是否能完全滿足項目的需求。DAO是一種容易使用并且功能強大的框架能夠處理存儲機制的改變。你在前期作出了付出,但是它肯定會在最后對你有幫助的。
資源·Matrix-Java開發者社區:
http://www.matrix.org.cn·onjava.com:
onjava.com·這篇文章的示例代碼:(注:與譯文放入同一壓縮包中):
http://www.onjava.com/onjava/2005/08/10/examples/SampleDAO.zip·iBatis主頁:
http://ibatis.apache.org/·核心 J2EE 模式:數據存儲對象:
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html·Hibernate主頁 (或者CodeZoo: Hibernate) :
http://www.hibernate.org/·使用XStream序列化Java對象:
http://www.xml.com/pub/a/2004/08/18/xstream.html????????
·XStream (或者 CodeZoo: XStream) :http://xstream.codehaus.org/ ( http://www.codezoo.com/ :http://www.codezoo.com/pub/component/3551 )
關于作者Sunil Patil對J2EE技術領域的研究超過5年時間。他對感興趣的領域是與對象相關的映射工具、UI框架和Portals