IBATIS Developer’s Guide Version 1.0
September 2, 2004 So many open source projects. Why not Open your Documents? OpenDoc Series’ iBATIS 2.0 開(kāi)發(fā)指南 V1.0 作者:夏昕 xiaxin(at)gmail.com So many open source projects. Why not Open your Documents? J IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 文檔說(shuō)明 參與人員: 作者 聯(lián)絡(luò) 夏昕 xiaxin(at)gmail.com (at) 為email @ 符號(hào) 發(fā)布記錄 版本 日期 作者 說(shuō)明 0.0 2004.8.1 夏昕 第一版 1.0 2004.9.1 夏昕 補(bǔ)充ibatis in Spring部分 OpenDoc版權(quán)說(shuō)明 本文檔版權(quán)歸原作者所有。 在免費(fèi)、且無(wú)任何附加條件的前提下,可在網(wǎng)絡(luò)媒體中自由傳播。 如需部分或者全文引用,請(qǐng)事先征求作者意見(jiàn)。 如果本文對(duì)您有些許幫助,表達(dá)謝意的最好方式,是將您發(fā)現(xiàn)的問(wèn)題和文檔改進(jìn)意見(jiàn)及時(shí)反饋給 作者。當(dāng)然,倘若有時(shí)間和能力,能為技術(shù)群體無(wú)償貢獻(xiàn)自己的所學(xué)為最好的回饋。 另外,筆者近來(lái)試圖就日本、印度的軟件開(kāi)發(fā)模式進(jìn)行一些調(diào)研。如果諸位可以贈(zèng)閱日本、印度 軟件研發(fā)過(guò)程中的需求、設(shè)計(jì)文檔以供研究,感激不盡! IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? ibatis 開(kāi)發(fā)指南 ibatis Quick Start............................................................................................5 準(zhǔn)備工作..........................................................................................................5 構(gòu)建ibatis基礎(chǔ)代碼....................................................................................5 ibatis配置........................................................................................................... 11 ibatis基礎(chǔ)語(yǔ)義......................................................................................................16 XmlSqlMapClientBuilder...................................................................16 SqlMapClient ...........................................................................................16 SqlMapClient基本操作示例..........................................................16 OR映射...........................................................................................................19 ibatis高級(jí)特性......................................................................................................26 數(shù)據(jù)關(guān)聯(lián)........................................................................................................26 一對(duì)多關(guān)聯(lián)............................................................................................26 一對(duì)一關(guān)聯(lián)............................................................................................28 延遲加載........................................................................................................30 動(dòng)態(tài)映射........................................................................................................31 事務(wù)管理........................................................................................................35 基于JDBC的事務(wù)管理機(jī)制................................................................35 基于JTA的事務(wù)管理機(jī)制...................................................................36 外部事務(wù)管理.........................................................................................38 Cache..............................................................................................................39 MEMORY類(lèi)型Cache與WeakReference ........................................40 LRU型Cache .......................................................................................42 FIFO型Cache ......................................................................................43 OSCache.................................................................................................43 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? ibatis 開(kāi)發(fā)指南 相對(duì)Hibernate和Apache OJB 等“一站式”ORM解決方案而言,ibatis 是一種“半 自動(dòng)化”的ORM實(shí)現(xiàn)。 所謂“半自動(dòng)”,可能理解上有點(diǎn)生澀。縱觀目前主流的ORM,無(wú)論Hibernate 還是 Apache OJB,都對(duì)數(shù)據(jù)庫(kù)結(jié)構(gòu)提供了較為完整的封裝,提供了從POJO 到數(shù)據(jù)庫(kù)表的全 套映射機(jī)制。程序員往往只需定義好了POJO 到數(shù)據(jù)庫(kù)表的映射關(guān)系,即可通過(guò)Hibernate 或者OJB 提供的方法完成持久層操作。程序員甚至不需要對(duì)SQL 的熟練掌握, Hibernate/OJB 會(huì)根據(jù)制定的存儲(chǔ)邏輯,自動(dòng)生成對(duì)應(yīng)的SQL 并調(diào)用JDBC 接口加以執(zhí) 行。 大多數(shù)情況下(特別是對(duì)新項(xiàng)目,新系統(tǒng)的開(kāi)發(fā)而言),這樣的機(jī)制無(wú)往不利,大有一 統(tǒng)天下的勢(shì)頭。但是,在一些特定的環(huán)境下,這種一站式的解決方案卻未必靈光。 在筆者的系統(tǒng)咨詢(xún)工作過(guò)程中,常常遇到以下情況: 1. 系統(tǒng)的部分或全部數(shù)據(jù)來(lái)自現(xiàn)有數(shù)據(jù)庫(kù),處于安全考慮,只對(duì)開(kāi)發(fā)團(tuán)隊(duì)提供幾 條Select SQL(或存儲(chǔ)過(guò)程)以獲取所需數(shù)據(jù),具體的表結(jié)構(gòu)不予公開(kāi)。 2. 開(kāi)發(fā)規(guī)范中要求,所有牽涉到業(yè)務(wù)邏輯部分的數(shù)據(jù)庫(kù)操作,必須在數(shù)據(jù)庫(kù)層由 存儲(chǔ)過(guò)程實(shí)現(xiàn)(就筆者工作所面向的金融行業(yè)而言,工商銀行、中國(guó)銀行、交 通銀行,都在開(kāi)發(fā)規(guī)范中嚴(yán)格指定) 3. 系統(tǒng)數(shù)據(jù)處理量巨大,性能要求極為苛刻,這往往意味著我們必須通過(guò)經(jīng)過(guò)高 度優(yōu)化的SQL語(yǔ)句(或存儲(chǔ)過(guò)程)才能達(dá)到系統(tǒng)性能設(shè)計(jì)指標(biāo)。 面對(duì)這樣的需求,再次舉起Hibernate 大刀,卻發(fā)現(xiàn)刀鋒不再銳利,甚至無(wú)法使用, 奈何?恍惚之際,只好再摸出JDBC 準(zhǔn)備拼死一搏……,說(shuō)得未免有些凄涼,直接使用JDBC 進(jìn)行數(shù)據(jù)庫(kù)操作實(shí)際上也是不錯(cuò)的選擇,只是拖沓的數(shù)據(jù)庫(kù)訪問(wèn)代碼,乏味的字段讀取操作 令人厭煩。 “半自動(dòng)化”的ibatis,卻剛好解決了這個(gè)問(wèn)題。 這里的“半自動(dòng)化”,是相對(duì)Hibernate等提供了全面的數(shù)據(jù)庫(kù)封裝機(jī)制的“全自動(dòng)化” ORM 實(shí)現(xiàn)而言,“全自動(dòng)”ORM 實(shí)現(xiàn)了POJO 和數(shù)據(jù)庫(kù)表之間的映射,以及SQL 的自動(dòng) 生成和執(zhí)行。而ibatis 的著力點(diǎn),則在于POJO 與SQL之間的映射關(guān)系。也就是說(shuō),ibatis 并不會(huì)為程序員在運(yùn)行期自動(dòng)生成SQL 執(zhí)行。具體的SQL 需要程序員編寫(xiě),然后通過(guò)映 射配置文件,將SQL所需的參數(shù),以及返回的結(jié)果字段映射到指定POJO。 使用ibatis 提供的ORM機(jī)制,對(duì)業(yè)務(wù)邏輯實(shí)現(xiàn)人員而言,面對(duì)的是純粹的Java對(duì)象, 這一層與通過(guò)Hibernate 實(shí)現(xiàn)ORM 而言基本一致,而對(duì)于具體的數(shù)據(jù)操作,Hibernate 會(huì)自動(dòng)生成SQL 語(yǔ)句,而ibatis 則要求開(kāi)發(fā)者編寫(xiě)具體的SQL 語(yǔ)句。相對(duì)Hibernate等 “全自動(dòng)”ORM機(jī)制而言,ibatis 以SQL開(kāi)發(fā)的工作量和數(shù)據(jù)庫(kù)移植性上的讓步,為系統(tǒng) 設(shè)計(jì)提供了更大的自由空間。作為“全自動(dòng)”ORM 實(shí)現(xiàn)的一種有益補(bǔ)充,ibatis 的出現(xiàn)顯 得別具意義。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? ibatis Quick Start 準(zhǔn)備工作 1. 下載ibatis軟件包(http://www.)。 2. 創(chuàng)建測(cè)試數(shù)據(jù)庫(kù),并在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)t_user 表,其中包含三個(gè)字段: Ø id(int) Ø name(varchar) Ø sex(int)。 3. 為了在開(kāi)發(fā)過(guò)程更加直觀,我們需要將ibatis日志打開(kāi)以便觀察ibatis運(yùn)作的細(xì)節(jié)。 ibatis 采用Apache common_logging,并結(jié)合Apache log4j 作為日志輸出組件。在 CLASSPATH 中新建log4j.properties配置文件,內(nèi)容如下: log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n log4j.logger.java.sql.PreparedStatement=DEBUG 構(gòu)建ibatis基礎(chǔ)代碼 ibatis 基礎(chǔ)代碼包括: 1. ibatis 實(shí)例配置 一個(gè)典型的配置文件如下(具體配置項(xiàng)目的含義見(jiàn)后): <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www./dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" errorTracingEnabled="true" maxRequests="32" maxSessions="10" maxTransactions="5" useStatementNamespaces="false" /> <transactionManager type="JDBC"> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="com.p6spy.engine.spy.P6SpyDriver"/> <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/sample"/> <property name="JDBC.Username" value="user"/> <property name="JDBC.Password" value="mypass"/> <property name="Pool.MaximumActiveConnections" value="10"/> <property name="Pool.MaximumIdleConnections" value="5"/> <property name="Pool.MaximumCheckoutTime" value="120000"/> <property name="Pool.TimeToWait" value="500"/> <property name="Pool.PingQuery" value="select 1 from ACCOUNT"/> <property name="Pool.PingEnabled" value="false"/> <property name="Pool.PingConnectionsOlderThan" value="1"/> <property name="Pool.PingConnectionsNotUsedFor" value="1"/> </dataSource> </transactionManager> <sqlMap resource="com/ibatis/sample/User.xml"/> </sqlMapConfig> 2. POJO(Plain Ordinary Java Object) 下面是我們用作示例的一個(gè)POJO: public class User implements Serializable { private Integer id; private String name; private Integer sex; private Set addresses = new HashSet(); /** default constructor */ public User() { } public Integer getId() { return this.id; IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? } public void setId(Integer id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getSex() { return this.sex; } public void setSex(Integer sex) { this.sex = sex; } } 3. 映射文件 與Hibernate 不同。因?yàn)樾枰斯ぞ帉?xiě)SQL 代碼,ibatis 的映射文件一般采 用手動(dòng)編寫(xiě)(通過(guò)Copy/Paste,手工編寫(xiě)映射文件也并沒(méi)想象中的麻煩)。 針對(duì)上面POJO 的映射代碼如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www./dtd/sql-map-2.dtd"> <sqlMap namespace="User"> <typeAlias alias="user" type="com.ibatis.sample.User"/> <select id="getUser" parameterClass="java.lang.String" resultClass="user"> <![CDATA[ select name, sex from t_user IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? where name = #name# ]]> </select> <update id="updateUser" parameterClass="user"> <![CDATA[ UPDATE t_user SET name=#name#, sex=#sex# WHERE id = #id# ]]> </update> <insert id="insertUser" parameterClass="user" > INSERT INTO t_user ( name, sex) VALUES ( #name#, #sex# ) </insert> <delete id="deleteUser" parameterClass="java.lang.String"> delete from t_user where id = #value# </delete> </sqlMap> 從上面的映射文件可以看出,通過(guò)<insert>、<delete>、<update>、 <select>四個(gè)節(jié)點(diǎn),我們分別定義了針對(duì)TUser 對(duì)象的增刪改查操作。在這 四個(gè)節(jié)點(diǎn)中,我們指定了對(duì)應(yīng)的SQL 語(yǔ)句,以u(píng)pdate節(jié)點(diǎn)為例: …… <update id="updateUser" ⑴ parameterClass="user"> ⑵ <![CDATA[ ⑶ UPDATE t_user ⑷ SET ( IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? name=#name#, ⑸ sex=#sex# ⑹ ) WHERE id = #id# ⑺ ]]> </update> …… ⑴ ID 指定了操作ID,之后我們可以在代碼中通過(guò)指定操作id 來(lái)執(zhí)行此節(jié)點(diǎn)所定 義的操作,如: sqlMap.update("updateUser",user); ID設(shè)定使得在一個(gè)配置文件中定義兩個(gè)同名節(jié)點(diǎn)成為可能(兩個(gè)update節(jié) 點(diǎn),以不同id區(qū)分) ⑵ parameterClass 指定了操作所需的參數(shù)類(lèi)型, 此例中update 操作以 com.ibatis.sample.User 類(lèi)型的對(duì)象作為參數(shù),目標(biāo)是將提供的User 實(shí)例更新到數(shù)據(jù)庫(kù)。 parameterClass="user"中,user為“com.ibatis.sample.User” 類(lèi)的別名,別名可通過(guò)typeAlias節(jié)點(diǎn)指定,如示例配置文件中的: <typeAlias alias="user" type="com.ibatis.sample.User"/> ⑶ <![CDATA[……]]> 通過(guò)<![CDATA[……]]>節(jié)點(diǎn),可以避免SQL 中與XML 規(guī)范相沖突的字符對(duì) XML映射文件的合法性造成影響。 ⑷ 執(zhí)行更新操作的SQL,這里的SQL 即實(shí)際數(shù)據(jù)庫(kù)支持的SQL 語(yǔ)句,將由 ibatis填入?yún)?shù)后交給數(shù)據(jù)庫(kù)執(zhí)行。 ⑸ SQL中所需的用戶(hù)名參數(shù),“#name#”在運(yùn)行期會(huì)由傳入的user對(duì)象的name 屬性填充。 ⑹ SQL 中所需的用戶(hù)性別參數(shù)“#sex#”,將在運(yùn)行期由傳入的user 對(duì)象的 sex屬性填充。 ⑺ SQL中所需的條件參數(shù)“#id#”,將在運(yùn)行期由傳入的user對(duì)象的id屬性 填充。 對(duì)于這個(gè)示例,ibatis在運(yùn)行期會(huì)讀取id 為“updateUser”的update節(jié)點(diǎn) 的SQL定義,并調(diào)用指定的user對(duì)象的對(duì)應(yīng)getter方法獲取屬性值,并用此 屬性值,對(duì)SQL中的參數(shù)進(jìn)行填充后提交數(shù)據(jù)庫(kù)執(zhí)行。 此例對(duì)應(yīng)的應(yīng)用級(jí)代碼如下,其中演示了ibatis SQLMap的基本使用方法: String resource ="com/ibatis/sample/SqlMapConfig.xml"; Reader reader; IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder = new XmlSqlMapClientBuilder(); SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader); //sqlMap系統(tǒng)初始化完畢,開(kāi)始執(zhí)行update操作 try{ sqlMap.startTransaction(); User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(1)); sqlMap.update("updateUser",user); sqlMap.commitTransaction(); finally{ sqlMap.endTransaction(); } 其中,SqlMapClient是ibatis運(yùn)作的核心,所有操作均通過(guò)SqlMapClient 實(shí)例完成。 可以看出,對(duì)于應(yīng)用層而言,程序員面對(duì)的是傳統(tǒng)意義上的數(shù)據(jù)對(duì)象,而非JDBC 中煩雜的ResultSet,這使得上層邏輯開(kāi)發(fā)人員的工作量大大減輕,同時(shí)代碼更 加清晰簡(jiǎn)潔。 數(shù)據(jù)庫(kù)操作在映射文件中加以定義,從而將數(shù)據(jù)存儲(chǔ)邏輯從上層邏輯代碼中獨(dú)立 出來(lái)。 而底層數(shù)據(jù)操作的SQL可配置化,使得我們可以控制最終的數(shù)據(jù)操作方式,通過(guò) SQL的優(yōu)化獲得最佳的數(shù)據(jù)庫(kù)執(zhí)行效能,這在依賴(lài)SQL自動(dòng)生成的“全自動(dòng)”ORM 機(jī)制中是所難以實(shí)現(xiàn)的。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? ibatis配置 結(jié)合上面示例中的ibatis配置文件。下面是對(duì)配置文件中各節(jié)點(diǎn)的說(shuō)明: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www./dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings ⑴ cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" errorTracingEnabled="true" maxRequests="32" maxSessions="10" maxTransactions="5" useStatementNamespaces="false" /> <transactionManager type="JDBC"> ⑵ <dataSource type="SIMPLE"> ⑶ <property name="JDBC.Driver" value="com.p6spy.engine.spy.P6SpyDriver"/> <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/sample"/> <property name="JDBC.Username" value="user"/> <property name="JDBC.Password" value="mypass"/> <property name="Pool.MaximumActiveConnections" value="10"/> <property name="Pool.MaximumIdleConnections" value="5"/> <property name="Pool.MaximumCheckoutTime" value="120000"/> <property name="Pool.TimeToWait" value="500"/> <property name="Pool.PingQuery" value="select 1 from ACCOUNT"/> <property name="Pool.PingEnabled" value="false"/> <property name="Pool.PingConnectionsOlderThan" value="1"/> <property name="Pool.PingConnectionsNotUsedFor" value="1"/> </dataSource> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? </transactionManager> <sqlMap resource="com/ibatis/sample/User.xml"/> ⑷ <sqlMap resource="com/ibatis/sample/Address.xml"/> </sqlMapConfig> ⑴ Settings 節(jié)點(diǎn) 參數(shù) 描述 cacheModelsEnabled 是否啟用SqlMapClient上的緩存機(jī)制。 建議設(shè)為"true" enhancementEnabled 是否針對(duì)POJO啟用字節(jié)碼增強(qiáng)機(jī)制以提升 getter/setter的調(diào)用效能,避免使用Java Reflect所帶來(lái)的性能開(kāi)銷(xiāo)。 同時(shí),這也為L(zhǎng)azy Loading帶來(lái)了極大的性能 提升。 建議設(shè)為"true" errorTracingEnabled 是否啟用錯(cuò)誤日志,在開(kāi)發(fā)期間建議設(shè)為"true" 以方便調(diào)試 lazyLoadingEnabled 是否啟用延遲加載機(jī)制,建議設(shè)為"true" maxRequests 最大并發(fā)請(qǐng)求數(shù)(Statement并發(fā)數(shù)) maxTransactions 最大并發(fā)事務(wù)數(shù) maxSessions 最大Session 數(shù)。即當(dāng)前最大允許的并發(fā) SqlMapClient數(shù)。 maxSessions設(shè)定必須介于 maxTransactions和maxRequests之間,即 maxTransactions<maxSessions=< maxRequests useStatementNamespaces 是否使用Statement命名空間。 這里的命名空間指的是映射文件中,sqlMap節(jié)點(diǎn) 的namespace屬性,如在上例中針對(duì)t_user 表的映射文件sqlMap節(jié)點(diǎn): <sqlMap namespace="User"> 這里,指定了此sqlMap節(jié)點(diǎn)下定義的操作均從 屬于"User"命名空間。 在useStatementNamespaces="true"的情 況下,Statement調(diào)用需追加命名空間,如: IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? sqlMap.update("User.updateUser",use r); 否則直接通過(guò)Statement名稱(chēng)調(diào)用即可,如: sqlMap.update("updateUser",user); 但請(qǐng)注意此時(shí)需要保證所有映射文件中, Statement定義無(wú)重名。 ⑵ transactionManager節(jié)點(diǎn) transactionManager 節(jié)點(diǎn)定義了ibatis 的事務(wù)管理器,目前提供了以下幾 種選擇: Ø JDBC 通過(guò)傳統(tǒng)JDBC Connection.commit/rollback實(shí)現(xiàn)事務(wù)支持。 Ø JTA 使用容器提供的JTA服務(wù)實(shí)現(xiàn)全局事務(wù)管理。 Ø EXTERNAL 外部事務(wù)管理,如在EJB中使用ibatis,通過(guò)EJB的部署配置即可實(shí)現(xiàn)自 動(dòng)的事務(wù)管理機(jī)制。此時(shí)ibatis 將把所有事務(wù)委托給外部容器進(jìn)行管理。 此外,通過(guò)Spring 等輕量級(jí)容器實(shí)現(xiàn)事務(wù)的配置化管理也是一個(gè)不錯(cuò)的選 擇。關(guān)于結(jié)合容器實(shí)現(xiàn)事務(wù)管理,參見(jiàn)“高級(jí)特性”中的描述。 ⑶ dataSource節(jié)點(diǎn) dataSource從屬于transactionManager節(jié)點(diǎn),用于設(shè)定ibatis運(yùn)行期使 用的DataSource屬性。 type屬性: dataSource節(jié)點(diǎn)的type屬性指定了dataSource的實(shí)現(xiàn)類(lèi)型。 可選項(xiàng)目: Ø SIMPLE: SIMPLE是ibatis內(nèi)置的dataSource實(shí)現(xiàn),其中實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的 數(shù)據(jù)庫(kù)連接池機(jī)制, 對(duì)應(yīng)ibatis 實(shí)現(xiàn)類(lèi)為 com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory。 Ø DBCP: 基于Apache DBCP 連接池組件實(shí)現(xiàn)的DataSource 封裝,當(dāng)無(wú)容器提 供DataSource 服務(wù)時(shí),建議使用該選項(xiàng),對(duì)應(yīng)ibatis 實(shí)現(xiàn)類(lèi)為 com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory。 Ø JNDI: 使用J2EE 容器提供的DataSource 實(shí)現(xiàn),DataSource 將通過(guò)指定 的JNDI Name 從容器中獲取。對(duì)應(yīng)ibatis 實(shí)現(xiàn)類(lèi)為 com.ibatis.sqlmap.engine.datasource.JndiDataSourceFacto ry。 dataSource的子節(jié)點(diǎn)說(shuō)明(SIMPLE&DBCP): IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 參數(shù) 描述 JDBC.Driver JDBC 驅(qū)動(dòng)。 如:org.gjt.mm.mysql.Driver JDBC.ConnectionURL 數(shù)據(jù)庫(kù)URL。 如:jdbc:mysql://localhost/sample 如果用的是SQLServer JDBC Driver,需要 在url后追加SelectMethod=Cursor以獲得 JDBC事務(wù)的多Statement支持。 JDBC.Username 數(shù)據(jù)庫(kù)用戶(hù)名 JDBC.Password 數(shù)據(jù)庫(kù)用戶(hù)密碼 Pool.MaximumActiveConn ections 數(shù)據(jù)庫(kù)連接池可維持的最大容量。 Pool.MaximumIdleConnec tions 數(shù)據(jù)庫(kù)連接池中允許的掛起(idle)連接數(shù)。 以上子節(jié)點(diǎn)適用于SIMPLE 和DBCP 模式,分別針對(duì)SIMPLE 和DBCP 模式的 DataSource私有配置節(jié)點(diǎn)如下: SIMPLE: 參數(shù) 描述 Pool.MaximumCheckoutTi me 數(shù)據(jù)庫(kù)聯(lián)接池中,連接被某個(gè)任務(wù)所允許占用的 最大時(shí)間,如果超過(guò)這個(gè)時(shí)間限定,連接將被強(qiáng) 制收回。(毫秒) Pool.TimeToWait 當(dāng)線程試圖從連接池中獲取連接時(shí),連接池中無(wú) 可用連接可供使用,此時(shí)線程將進(jìn)入等待狀態(tài), 直到池中出現(xiàn)空閑連接。此參數(shù)設(shè)定了線程所允 許等待的最長(zhǎng)時(shí)間。(毫秒) Pool.PingQuery 數(shù)據(jù)庫(kù)連接狀態(tài)檢測(cè)語(yǔ)句。 某些數(shù)據(jù)庫(kù)在連接在某段時(shí)間持續(xù)處于空閑狀態(tài) 時(shí)會(huì)將其斷開(kāi)。而連接池管理器將通過(guò)此語(yǔ)句檢 測(cè)池中連接是否可用。 檢測(cè)語(yǔ)句應(yīng)該是一個(gè)最簡(jiǎn)化的無(wú)邏輯SQL。 如“select 1 from t_user”,如果執(zhí)行此語(yǔ)句 成功,連接池管理器將認(rèn)為此連接處于可用狀態(tài)。 Pool.PingEnabled 是否允許檢測(cè)連接狀態(tài)。 Pool.PingConnectionsOl derThan 對(duì)持續(xù)連接時(shí)間超過(guò)設(shè)定值(毫秒)的連接進(jìn)行 檢測(cè)。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? Pool.PingConnectionsNo tUsedFor 對(duì)空閑超過(guò)設(shè)定值(毫秒)的連接進(jìn)行檢測(cè)。 DBCP: 參數(shù) 描述 Pool.MaximumWait 當(dāng)線程試圖從連接池中獲取連接時(shí),連接池中無(wú) 可用連接可供使用,此時(shí)線程將進(jìn)入等待狀態(tài), 直到池中出現(xiàn)空閑連接。此參數(shù)設(shè)定了線程所允 許等待的最長(zhǎng)時(shí)間。(毫秒) Pool.ValidationQuery 數(shù)據(jù)庫(kù)連接狀態(tài)檢測(cè)語(yǔ)句。 某些數(shù)據(jù)庫(kù)在連接在某段時(shí)間持續(xù)處于空閑狀態(tài) 時(shí)會(huì)將其斷開(kāi)。而連接池管理器將通過(guò)此語(yǔ)句檢 測(cè)池中連接是否可用。 檢測(cè)語(yǔ)句應(yīng)該是一個(gè)最簡(jiǎn)化的無(wú)邏輯SQL。 如“select 1 from t_user”,如果執(zhí)行此語(yǔ)句 成功,連接池管理器將認(rèn)為此連接處于可用狀態(tài)。 Pool.LogAbandoned 當(dāng)數(shù)據(jù)庫(kù)連接被廢棄時(shí),是否打印日志。 Pool.RemoveAbandonedTi meout 數(shù)據(jù)庫(kù)連接被廢棄的最大超時(shí)時(shí)間 Pool.RemoveAbandoned 當(dāng)連接空閑時(shí)間超過(guò) RemoveAbandonedTimeout時(shí),是否將其廢 棄。 JNDI由于大部分配置是在應(yīng)用服務(wù)器中進(jìn)行,因此ibatis中的配置相對(duì)簡(jiǎn)單,下面 是分別使用JDBC和JTA事務(wù)管理的JDNI配置: 使用JDBC事務(wù)管理的JNDI DataSource配置 <transactionManager type="JDBC" > <dataSource type="JNDI"> <property name="DataSource" value="java:comp/env/jdbc/myDataSource"/> </dataSource> </transactionManager> <transactionManager type="JTA" > <property name="UserTransaction" value="java:/ctx/con/UserTransaction"/> <dataSource type="JNDI"> <property name="DataSource" value="java:comp/env/jdbc/myDataSource"/> </dataSource> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? </transactionManager> ⑷ sqlMap節(jié)點(diǎn) sqlMap 節(jié)點(diǎn)指定了映射文件的位置,配置中可出現(xiàn)多個(gè)sqlMap 節(jié)點(diǎn),以指定 項(xiàng)目?jī)?nèi)所包含的所有映射文件。 ibatis基礎(chǔ)語(yǔ)義 XmlSqlMapClientBuilder XmlSqlMapClientBuilder是ibatis 2.0之后版本新引入的組件,用以替代1.x 版本中的XmlSqlMapBuilder。其作用是根據(jù)配置文件創(chuàng)建SqlMapClient實(shí)例。 SqlMapClient SqlMapClient是ibatis的核心組件,提供數(shù)據(jù)操作的基礎(chǔ)平臺(tái)。SqlMapClient 可通過(guò)XmlSqlMapClientBuilder創(chuàng)建: String resource ="com/ibatis/sample/SqlMapConfig.xml"; Reader reader; reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder = new XmlSqlMapClientBuilder(); SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader); "com/ibatis/sample/SqlMapConfig.xml"指明了配置文件在CLASSPATH 中的相對(duì)路徑。XmlSqlMapClientBuilder通過(guò)接受一個(gè)Reader類(lèi)型的配置文 件句柄,根據(jù)配置參數(shù),創(chuàng)建SqlMapClient實(shí)例。 SqlMapClient提供了眾多數(shù)據(jù)操作方法,下面是一些常用方法的示例,具體說(shuō)明 文檔請(qǐng)參見(jiàn)ibatis java doc,或者ibatis官方開(kāi)發(fā)手冊(cè)。 SqlMapClient基本操作示例 以下示例摘自ibatis官方開(kāi)發(fā)手冊(cè),筆者對(duì)其進(jìn)行了重新排版以獲得更好的閱讀效果。 例1: 數(shù)據(jù)寫(xiě)入操作(insert, update, delete): sqlMap.startTransaction(); Product product = new Product(); product.setId (1); product.setDescription (“Shih Tzu”); int rows = sqlMap.insert (“insertProduct”, product); sqlMap.commitTransaction(); IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 例2: 數(shù)據(jù)查詢(xún) (select) sqlMap.startTransaction(); Integer key = new Integer (1); Product product = (Product)sqlMap.queryForObject (“getProduct”, key); sqlMap.commitTransaction(); 例3: 在指定對(duì)象中存放查詢(xún)結(jié)果(select) sqlMap.startTransaction(); Customer customer = new Customer(); sqlMap.queryForObject(“getCust”, parameterObject, customer); sqlMap.queryForObject(“getAddr”, parameterObject, customer); sqlMap.commitTransaction(); 例4: 執(zhí)行批量查詢(xún) (select) sqlMap.startTransaction(); List list = sqlMap.queryForList (“getProductList”, null); sqlMap.commitTransaction(); 例5: 關(guān)于AutoCommit //沒(méi)有預(yù)先執(zhí)行startTransaction時(shí),默認(rèn)為auto_commit模式 int rows = sqlMap.insert (“insertProduct”, product); 例6:查詢(xún)指定范圍內(nèi)的數(shù)據(jù) sqlMap.startTransaction(); List list = sqlMap.queryForList (“getProductList”, null, 0, 40); sqlMap.commitTransaction(); 例7: 結(jié)合RowHandler進(jìn)行查詢(xún)(select) public class MyRowHandler implements RowHandler { public void handleRow (Object object, List list) throws SQLException { Product product = (Product) object; product.setQuantity (10000); sqlMap.update (“updateProduct”, product); } } sqlMap.startTransaction(); RowHandler rowHandler = new MyRowHandler(); List list = sqlMap.queryForList (“getProductList”, null, rowHandler); sqlMap.commitTransaction(); IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 例8: 分頁(yè)查詢(xún) (select) PaginatedList list = sqlMap.queryForPaginatedList (“getProductList”, null, 10); list.nextPage(); list.previousPage(); 例9: 基于Map的批量查詢(xún) (select) sqlMap.startTransaction(); Map map = sqlMap.queryForMap (“getProductList”, null, “productCode”); sqlMap.commitTransaction(); Product p = (Product) map.get(“EST-93”); IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? OR 映射 相對(duì)Hibernate 等ORM 實(shí)現(xiàn)而言,ibatis的映射配置更為簡(jiǎn)潔直接,下面是一 個(gè)典型的配置文件。 <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www./dtd/sql-map-2.dtd"> <sqlMap namespace="User"> <!--模塊配置--> <typeAlias alias="user" type="com.ibatis.sample.User"/> <cacheModel id="userCache" type="LRU"> <flushInterval hours="24"/> <flushOnExecute statement=" updateUser"/> <property name="size" value="1000" /> </cacheModel> <!—Statement配置--> <select id="getUser" parameterClass="java.lang.String" resultClass="user" cacheModel="userCache" > <![CDATA[ select name, sex from t_user where name = #name# ]]> </select> <update id="updateUser" parameterClass="user"> UPDATE t_user SET name=#name#, sex=#sex# WHERE id = #id# </update> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? </sqlMap> 可以看到,映射文件主要分為兩個(gè)部分:模塊配置和Statement配置。 模塊配置包括: Ø typeAlias節(jié)點(diǎn): 定義了本映射文件中的別名,以避免過(guò)長(zhǎng)變量值的反復(fù)書(shū)寫(xiě),此例中通過(guò) typeAlias節(jié)點(diǎn)為類(lèi)"com.ibatis.sample.User"定義了一個(gè)別名"user", 這樣在本配置文件的其他部分,需要引用"com.ibatis.sample.User"類(lèi)時(shí), 只需以其別名替代即可。 Ø cacheModel節(jié)點(diǎn) 定義了本映射文件中使用的Cache機(jī)制: <cacheModel id="userCache" type="LRU"> <flushInterval hours="24"/> <flushOnExecute statement="updateUser"/> <property name="size" value="1000" /> </cacheModel> 這里申明了一個(gè)名為"userCache"的cacheModel,之后可以在 Statement申明中對(duì)其進(jìn)行引用: <select id="getUser" parameterClass="java.lang.String" resultClass="user" cacheModel="userCache" > 這表明對(duì)通過(guò)id為"getUser"的Select statement獲取的數(shù)據(jù),使用 cacheModel "userCache"進(jìn)行緩存。之后如果程序再次用此Statement 進(jìn)行數(shù)據(jù)查詢(xún),即直接從緩存中讀取查詢(xún)結(jié)果,而無(wú)需再去數(shù)據(jù)庫(kù)查詢(xún)。 cacheModel主要有下面幾個(gè)配置點(diǎn): l flushInterval : 設(shè)定緩存有效期,如果超過(guò)此設(shè)定值,則將此CacheModel的緩存清空。 l size: 本CacheModel中最大容納的數(shù)據(jù)對(duì)象數(shù)量。 l flushOnExecute: 指定執(zhí)行特定Statement時(shí),將緩存清空。如updateUser操作將更 新數(shù)據(jù)庫(kù)中的用戶(hù)信息,這將導(dǎo)致緩存中的數(shù)據(jù)對(duì)象與數(shù)據(jù)庫(kù)中的實(shí)際 數(shù)據(jù)發(fā)生偏差,因此必須將緩存清空以避免臟數(shù)據(jù)的出現(xiàn)。 關(guān)于Cache的深入探討,請(qǐng)參見(jiàn)“高級(jí)特性”中的相關(guān)章節(jié)。 Statement配置: IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? Statement配置包含了數(shù)個(gè)與SQL Statement相關(guān)的節(jié)點(diǎn),分別為: u statement u insert u delete u update u select u procedure 其中,statement最為通用,它可以替代其余的所有節(jié)點(diǎn)。除statement之外 的節(jié)點(diǎn)各自對(duì)應(yīng)了SQL中的同名操作(procedure對(duì)應(yīng)存儲(chǔ)過(guò)程)。 使用statement 定義所有操作固然可以達(dá)成目標(biāo),但缺乏直觀性,建議在實(shí)際 開(kāi)發(fā)中根據(jù)操作目的,各自選用對(duì)應(yīng)的節(jié)點(diǎn)名加以申明。一方面,使得配置文件 更加直觀,另一方面,也可借助DTD對(duì)節(jié)點(diǎn)申明進(jìn)行更有針對(duì)性的檢查,以避免 配置上的失誤。 各種類(lèi)型的Statement 配置節(jié)點(diǎn)的參數(shù)類(lèi)型基本一致,區(qū)別在于數(shù)量不同。如 insert、update、delete節(jié)點(diǎn)無(wú)需返回?cái)?shù)據(jù)類(lèi)型定義(總是int)。 主要的配置項(xiàng)如下: statement: <statement id="statementName" [parameterClass="some.class.Name"] [resultClass="some.class.Name"] [parameterMap="nameOfParameterMap"] [resultMap="nameOfResultMap"] [cacheModel="nameOfCache"] > select * from t_user where sex = [?|#propertyName#] order by [$simpleDynamic$] </statement> select: <select id="statementName" [parameterClass="some.class.Name"] [resultClass="some.class.Name"] [parameterMap="nameOfParameterMap"] [resultMap="nameOfResultMap"] [cacheModel="nameOfCache"] > select * from t_user where sex = [?|#propertyName#] order by [$simpleDynamic$] </select> Insert: IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? <insert id="statementName" [parameterClass="some.class.Name"] [parameterMap="nameOfParameterMap"] > insert into t_user (name,sex) values ([?|#propertyName#],[?|#propertyName#]) </insert> Update: <update id="statementName" [parameterClass="some.class.Name"] [parameterMap="nameOfParameterMap"] > UPDATE t_user SET name=[?|#propertyName#], sex=[?|#propertyName#] WHERE id = [?|#propertyName#] </update> Delete: <delete id="statementName" [parameterClass="some.class.Name"] [parameterMap="nameOfParameterMap"] > delete from t_user where id = [?|#propertyName#] </delete> 其中以“[]”包圍的部分為可能出現(xiàn)的配置欄目。 參數(shù) 描述 parameterClass 參數(shù)類(lèi)。指定了參數(shù)的完整類(lèi)名(包括包路徑)。 可通過(guò)別名避免每次重復(fù)書(shū)寫(xiě)冗長(zhǎng)的類(lèi)名。 resultClass 結(jié)果類(lèi)。指定結(jié)果類(lèi)型的完整類(lèi)名(包括包路徑) 可通過(guò)別名避免每次重復(fù)書(shū)寫(xiě)冗長(zhǎng)的類(lèi)名。 parameterMap 參數(shù)映射,需結(jié)合parameterMap節(jié)點(diǎn)對(duì)映射 關(guān)系加以定義。 對(duì)于存儲(chǔ)過(guò)程之外的statement而言,建議使用 parameterClass作為參數(shù)配置方式,一方面避 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 免了參數(shù)映射配置工作,另一方面其性能表現(xiàn)也 更加出色。 resultMap 結(jié)果映射,需結(jié)合resultMap節(jié)點(diǎn)對(duì)映射關(guān)系 加以定義。 cacheModel statement對(duì)應(yīng)的Cache模塊。 對(duì)于參數(shù)定義而言,盡量使用parameterClass,即直接將POJO 作為 statement 的調(diào)用參數(shù),這樣在SQL 中可以直接將POJO 的屬性作為參數(shù)加以 設(shè)定,如: <update id="updateUser" parameterClass="com.ibatis.sample.User"> UPDATE t_user SET name=#name#, sex=#sex# WHERE id = #id# </update> 這里將com.ibatis.sample.User類(lèi)設(shè)定為update statement的參數(shù),之后, 我們即可在SQL 中通過(guò)#propertyName#對(duì)POJO 的屬性進(jìn)行引用。如上例 中的: SET name=#name#, sex=#sex# WHERE id=#id# 運(yùn)行期,ibatis 將通過(guò)調(diào)用User 對(duì)象的getName、getSex 和getId 方法獲得相 應(yīng)的參數(shù)值,并將其作為SQL 的參數(shù)。 如果parameterClass 中設(shè)定的是jdk 的中的簡(jiǎn)單對(duì)象類(lèi)型,如String、 Integer,ibatis會(huì)直接將其作為SQL中的參數(shù)值。 我們也可以將包含了參數(shù)數(shù)據(jù)的Map對(duì)象傳遞給Statement,如: <update id="updateUser" parameterClass="java.util.Map"> UPDATE t_user SET name=#name#, sex=#sex# WHERE id = #id# </update> 這里傳入的參數(shù)就是一個(gè)Map對(duì)象,ibatis將以key ”name”、”sex”、”id”從中 提取對(duì)應(yīng)的參數(shù)值。 同樣的原理,我們也可以在resultMap中設(shè)定返回類(lèi)型為map。 <select id="getUser" parameterClass="java.lang.String" resultClass="java.util.Map"> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? <![CDATA[ select id, name, sex from t_user where id = #id# ]]> </select> 返回的結(jié)果將以各字段名為key保存在Map對(duì)象中返回。 在SQL中設(shè)定參數(shù)名時(shí),可以同時(shí)指定參數(shù)類(lèi)型,如: SET name=#name:VARCHAR#, sex=#sex:NUMERIC# WHERE id=#id:NUMERIC# 對(duì)于返回結(jié)果而言,如果是select語(yǔ)句,建議也采用resultClass進(jìn)行定義,如: <select id="getUser" parameterClass="java.lang.String" resultClass="user"> <![CDATA[ select name, sex from t_user where name = #name# ]]> </select> ibatis會(huì)自動(dòng)根據(jù)select語(yǔ)句中的字段名,調(diào)用對(duì)應(yīng)POJO 的set方法設(shè)定屬性 值,如上例中,ibatis會(huì)調(diào)用setName,setSex 方法將Select語(yǔ)句返回的數(shù)據(jù)賦 予相應(yīng)的POJO 實(shí)例。 有些時(shí)候,數(shù)據(jù)庫(kù)表中的字段名過(guò)于晦澀,而為了使得代碼更易于理解,我們 希望字段映射到POJO時(shí),采用比較易讀的屬性名, 此時(shí),我們可以通過(guò)Select 的as 字句對(duì)字段名進(jìn)行轉(zhuǎn)義,如(假設(shè)我們的書(shū)庫(kù)中對(duì)應(yīng)用戶(hù)名的字段為 xingming,對(duì)應(yīng)性別的字段為xingbie): select xingming as name, xingbie as sex from t_user where id = #id# ibatis 會(huì)根據(jù)轉(zhuǎn)義后的字段名進(jìn)行屬性映射(即調(diào)用POJO 的setName 方法而 不是setXingming方法)。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? parameterMap和resultMap實(shí)現(xiàn)了POJO到數(shù)據(jù)庫(kù)字段的映射配置,下面是 一個(gè)例子: <resultMap id="get_user_result" class="user"> <result property="name" column="xingming" jdbcType="VARCHAR" javaType="java.lang.String"/> <result property="sex" column="xingbie" jdbcType="int" javaType="java.lang.Integer"/> <result property="id" column="id" jdbcType="int" javaType="java.lang.Integer"/> </resultMap> <parameterMap id="update_user_para" class="redemption" > <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="" /> <parameter property="sex" jdbcType="int" javaType="java.lang.Integer" nullValue="" /> </parameterMap> Parameter的nullValue指定了如果參數(shù)為空(null)時(shí)的默認(rèn)值。 之后我們即可在statement 申明中對(duì)其進(jìn)行引用,如: <procedure id="getUserList" resultMap="get_user_result" > {call sp_getUserList()} </procedure> <procedure id="doUserUpdate" parameterMap="update_user_para" > {call sp_doUserUpdate(#id#,#name#,#sex#)} </procedure> 一般而言,對(duì)于insert、update、delete、select語(yǔ)句,優(yōu)先采用parameterClass 和resultClass。 parameterMap 使用較少,而resultMap 則大多用于嵌套查詢(xún)以及存儲(chǔ)過(guò)程的 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 處理,之所以這樣,原因是由于存儲(chǔ)過(guò)程相對(duì)而言比較封閉(很多情況下需要調(diào)用現(xiàn)有 的存儲(chǔ)過(guò)程,其參數(shù)命名和返回的數(shù)據(jù)字段命名往往不符合Java編程中的命名習(xí)慣, 并且由于我們難以通過(guò)Select SQL的as子句進(jìn)行字段名轉(zhuǎn)義,無(wú)法使其自動(dòng)與POJO 中的屬性名相匹配)。此時(shí),使用resultMap建立字段名和POJO屬性名之間的映射 關(guān)系就顯得非常有效。另一方面,由于通過(guò)resultMap 指定了字段名和字段類(lèi)型, ibatis無(wú)需再通過(guò)JDBC ResultSetMetaData 來(lái)動(dòng)態(tài)獲取字段信息,在一定程度 上也提升了性能表現(xiàn)。 ibatis高級(jí)特性 數(shù)據(jù)關(guān)聯(lián) 至此,我們討論的都是針對(duì)獨(dú)立數(shù)據(jù)的操作。在實(shí)際開(kāi)發(fā)中,我們常常遇到關(guān)聯(lián)數(shù) 據(jù)的情況,如User 對(duì)象擁有若干Address 對(duì)象,每個(gè)Address 對(duì)象描述了對(duì)應(yīng)User 的 一個(gè)聯(lián)系地址,這種情況下,我們應(yīng)該如何處理? 通過(guò)單獨(dú)的Statement操作固然可以實(shí)現(xiàn)(通過(guò)Statement 用于讀取用戶(hù)數(shù)據(jù),再手 工調(diào)用另外一個(gè)Statement 根據(jù)用戶(hù)ID 返回對(duì)應(yīng)的Address信息)。不過(guò)這樣未免失之 繁瑣。下面我們就看看在ibatis 中,如何對(duì)關(guān)聯(lián)數(shù)據(jù)進(jìn)行操作。 ibatis 中,提供了Statement 嵌套支持,通過(guò)Statement 嵌套,我們即可實(shí)現(xiàn)關(guān)聯(lián)數(shù) 據(jù)的操作。 一對(duì)多關(guān)聯(lián) 下面的例子中,我們首選讀取t_user 表中的所有用戶(hù)記錄,然后獲取每個(gè)用戶(hù)對(duì)應(yīng) 的所有地址信息。 配置文件如下: <sqlMap namespace="User"> <typeAlias alias="user" type="com.ibatis.sample.User"/> <typeAlias alias="address" type="com.ibatis.sample.Address"/> <resultMap id="get-user-result" class="user"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="sex" column="sex"/> <result property="addresses" column="id" select="User.getAddressByUserId"/> </resultMap> <select id="getUsers" parameterClass="java.lang.String" resultMap="get-user-result"> <![CDATA[ select id, IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? name, sex from t_user where id = #id# ]]> </select> <select id="getAddressByUserId" parameterClass="int" resultClass="address"> <![CDATA[ select address, zipcode from t_address where user_id = #userid# ]]> </select> </sqlMap> 對(duì)應(yīng)代碼: String resource ="com/ibatis/sample/SqlMapConfig.xml"; Reader reader; reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder = new XmlSqlMapClientBuilder(); sqlMap = xmlBuilder.buildSqlMap(reader); //sqlMap系統(tǒng)初始化完畢 List userList = sqlMap.queryForList("User.getUsers", ""); for (int i = 0; i < userList.size(); i++) { User user = (User)userList.get(i); System.out.println("==>" + user.getName()); for (int k = 0; k < user.getAddresses().size(); k++) { Address addr = (Address) user.getAddresses().get(k); System.out.println(addr.getAddress()); } } IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 這里通過(guò)在resultMap 中定義嵌套查詢(xún)getAddressByUserId,我們實(shí)現(xiàn)了關(guān)聯(lián) 數(shù)據(jù)的讀取。 實(shí)際上,這種方式類(lèi)似于前面所說(shuō)的通過(guò)兩條單獨(dú)的Statement 進(jìn)行關(guān)聯(lián)數(shù)據(jù)的讀 取,只是將關(guān)聯(lián)關(guān)系在配置中加以描述,由ibatis自動(dòng)完成關(guān)聯(lián)數(shù)據(jù)的讀取。 需要注意的是,這里有一個(gè)潛在的性能問(wèn)題,也就是所謂“n+1”Select問(wèn)題。 注意上面示例運(yùn)行過(guò)程中的日志輸出: …… PreparedStatement - {pstm-100001} PreparedStatement: select id, name, sex from t_user …… PreparedStatement - {pstm-100004} PreparedStatement: select address, zipcode from t_address where user_id = ? …… PreparedStatement - {pstm-100007} PreparedStatement: select address,zipcode from t_address where user_id = ? 第一條PreparedStatement 將t_user 表中的所有數(shù)據(jù)讀取出來(lái)(目前t_user 表中有兩 條測(cè)試數(shù)據(jù)),隨即,通過(guò)兩次Select 操作,從t_address 表中讀取兩個(gè)用戶(hù)所關(guān)聯(lián)的 Address記錄。 如果t_user 表中記錄較少,不會(huì)有明顯的影響,假設(shè)t_user 表中有十萬(wàn)條記錄,那 么這樣的操作將需要100000+1 條Select語(yǔ)句反復(fù)執(zhí)行才能獲得結(jié)果,無(wú)疑,隨著記錄 的增長(zhǎng),這樣的開(kāi)銷(xiāo)將無(wú)法承受。 之所以在這里提及這個(gè)問(wèn)題,目的在于引起讀者的注意,在系統(tǒng)設(shè)計(jì)中根據(jù)具體情 況,采用一些規(guī)避手段(如使用存儲(chǔ)過(guò)程集中處理大批量關(guān)聯(lián)數(shù)據(jù)),從而避免因?yàn)檫@ 個(gè)問(wèn)題而引起產(chǎn)品品質(zhì)上的缺陷。 一對(duì)一關(guān)聯(lián) 一對(duì)一關(guān)聯(lián)是一對(duì)多關(guān)聯(lián)的一種特例。這種情況下,如果采用上面的示例將導(dǎo)致 1+1條SQL的執(zhí)行。 對(duì)于這種情況,我們可以采用一次Select兩張表的方式,避免這樣的性能開(kāi)銷(xiāo)(假 設(shè)上面示例中,每個(gè)User 只有一個(gè)對(duì)應(yīng)的Address記錄): <resultMap id="get-user-result" class="user"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="sex" column="sex"/> <result property="address" column="t_address.address"/> <result property="zipCode" column="t_address.zipcode"/> </resultMap> <select id="getUsers" parameterClass="java.lang.String" resultMap="get-user-result"> <![CDATA[ select IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? * from t_user,t_address where t_user.id=t_address.user_id ]]> </select> 與此同時(shí),應(yīng)該保證User 類(lèi)中包含address和zipCode兩個(gè)String型屬性。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 延遲加載 在運(yùn)行上面的例子時(shí),通過(guò)觀察期間的日志輸出順序我們可以發(fā)現(xiàn),在我們執(zhí)行 sqlMap.queryForList("User.getUsers", "")時(shí),實(shí)際上ibatis只向數(shù)據(jù)庫(kù)發(fā)送 了一條select id, name, sex from t_user SQL。而用于獲取Address記錄的SQL,只有在我 們真正訪問(wèn)address對(duì)象時(shí),才開(kāi)始執(zhí)行。 這也就是所謂的延遲加載(Lazy Loading)機(jī)制。即當(dāng)真正需要數(shù)據(jù)的時(shí)候,才加 載數(shù)據(jù)。延遲加載機(jī)制能為我們的系統(tǒng)性能帶來(lái)極大的提升。 試想,如果我們只需要獲取用戶(hù)名稱(chēng)和性別數(shù)據(jù),在沒(méi)有延遲加載特性的情況下, ibatis會(huì)一次將所有數(shù)據(jù)都從數(shù)據(jù)庫(kù)取回,包括用戶(hù)信息及其相關(guān)的地址數(shù)據(jù),而此時(shí), 關(guān)于地址數(shù)據(jù)的讀取操作沒(méi)有意義,也就是說(shuō),我們白白在地址數(shù)據(jù)的查詢(xún)讀取上浪費(fèi) 了大量的系統(tǒng)資源。延遲加載為我們妥善的處理了性能與編碼上的平衡(如果沒(méi)有延遲 加載,我們?yōu)榱吮苊鉄o(wú)謂的性能開(kāi)銷(xiāo),只能專(zhuān)門(mén)為此再增加一個(gè)不讀取地址信息的用戶(hù) 記錄檢索模塊,無(wú)疑增加了編碼上的工作量)。 回憶之前“ibatis配置”中的內(nèi)容: <settings ⑴ …… enhancementEnabled="true" lazyLoadingEnabled="true" …… /> Settings 節(jié)點(diǎn)有兩個(gè)與延遲加載相關(guān)的屬性lazyLoadingEnabled 和 enhancementEnabled,其中l(wèi)azyLoadingEnabled設(shè)定了系統(tǒng)是否使用延遲加載 機(jī)制,enhancementEnabled設(shè)定是否啟用字節(jié)碼強(qiáng)化機(jī)制(通過(guò)字節(jié)碼強(qiáng)化機(jī)制可 以為L(zhǎng)azy Loading帶來(lái)性能方面的改進(jìn)。 為了使用延遲加載所帶來(lái)的性能優(yōu)勢(shì),這兩項(xiàng)都建議設(shè)為"true"。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 動(dòng)態(tài)映射 在復(fù)雜查詢(xún)過(guò)程中,我們常常需要根據(jù)用戶(hù)的選擇決定查詢(xún)條件,這里發(fā)生變化的 并不只是SQL 中的參數(shù),包括Select 語(yǔ)句中所包括的字段和限定條件,都可能發(fā)生變 化。典型情況,如在一個(gè)復(fù)雜的組合查詢(xún)頁(yè)面,我們必須根據(jù)用戶(hù)的選擇和輸入決定查 詢(xún)的條件組合。 一個(gè)典型的頁(yè)面如下: 對(duì)于這個(gè)組合查詢(xún)頁(yè)面,根據(jù)用戶(hù)選擇填寫(xiě)的內(nèi)容,我們應(yīng)為其生成不同的查詢(xún)語(yǔ) 句。 如用戶(hù)沒(méi)有填寫(xiě)任何信息即提交查詢(xún)請(qǐng)求,我們應(yīng)該返回所有記錄: Select * from t_user; 如用戶(hù)只在頁(yè)面上填寫(xiě)了姓名“Erica”,我們應(yīng)該生成類(lèi)似: Select * from t_user where name like ‘%Erica%’ ; 的SQL查詢(xún)語(yǔ)句。 如用戶(hù)只在頁(yè)面上填寫(xiě)了地址“Beijing”,我們應(yīng)該生成類(lèi)似: Select * from t_user where address like ‘%Beijing%”; 的SQL。 而如果用戶(hù)同時(shí)填寫(xiě)了姓名和地址(”Erica”&’Beijing’),則我們應(yīng)生成類(lèi)似: Select * from t_user where name like ‘%Erica%’ and address like ‘%Beijing%” 的SQL查詢(xún)語(yǔ)句。 對(duì)于ibatis 這樣需要預(yù)先指定SQL 語(yǔ)句的ORM 實(shí)現(xiàn)而言,傳統(tǒng)的做法無(wú)非通過(guò) if-else 語(yǔ)句對(duì)輸入?yún)?shù)加以判定,然后針對(duì)用戶(hù)選擇調(diào)用不同的statement 定義。對(duì)于 上面這種簡(jiǎn)單的情況(兩種查詢(xún)條件的排列組合,共4 種情況)而言,statement 的重 復(fù)定義工作已經(jīng)讓人不厭其煩,而對(duì)于動(dòng)輒擁有七八個(gè)查詢(xún)條件,乃至十幾個(gè)查詢(xún)條件 的排列組合而言,瑣碎反復(fù)的statement定義實(shí)在讓人不堪承受。 考慮到這個(gè)問(wèn)題,ibatis引入了動(dòng)態(tài)映射機(jī)制,即在statement定義中,根據(jù)不同的 查詢(xún)參數(shù),設(shè)定對(duì)應(yīng)的SQL語(yǔ)句。 還是以上面的示例為例: <select id="getUsers" parameterClass="user" resultMap="get-user-result"> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? select id, name, sex from t_user <dynamic prepend="WHERE"> <isNotEmpty prepend="AND" property="name"> (name like #name#) </isNotEmpty> <isNotEmpty prepend="AND" property="address"> (address like #address#) </isNotEmpty> </dynamic> </select> 通過(guò)dynamic 節(jié)點(diǎn),我們定義了一個(gè)動(dòng)態(tài)的WHERE 子句。此WHERE 子句中將 可能包含兩個(gè)針對(duì)name 和address 字段的判斷條件。而這兩個(gè)字段是否加入檢索取決 于用戶(hù)所提供的查詢(xún)條件(字段是否為空[isNotEmpty])。 對(duì)于一個(gè)典型的Web程序而言,我們通過(guò)HttpServletRequest獲得表單中的字段名 并將其設(shè)入查詢(xún)參數(shù),如: user.setName(request.getParameter("name")); user.setAddress(request.getParameter("address")); sqlMap.queryForList("User.getUsers", user); 在執(zhí)行queryForList("User.getUsers", user)時(shí),ibatis 即根據(jù)配置文 件中設(shè)定的SQL動(dòng)態(tài)生成規(guī)則,創(chuàng)建相應(yīng)的SQL語(yǔ)句。 上面的示例中,我們通過(guò)判定節(jié)點(diǎn)isNotEmpty,指定了關(guān)于name 和address 屬 性的動(dòng)態(tài)規(guī)則: <isNotEmpty prepend="AND" property="name"> (name like #name#) </isNotEmpty> 這個(gè)節(jié)點(diǎn)對(duì)應(yīng)的語(yǔ)義是,如果參數(shù)類(lèi)的"name"屬性非空(isNotEmpty,即非空 字符串””),則在生成的SQL Where字句中包括判定條件(name like #name#),其 中#name#將以參數(shù)類(lèi)的name屬性值填充。 Address屬性的判定生成與name屬性完全相同,這里就不再贅述。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 這樣,我們通過(guò)在statement 定義中引入dynamic 節(jié)點(diǎn),很簡(jiǎn)單的實(shí)現(xiàn)了SQL 判 定子句的動(dòng)態(tài)生成,對(duì)于復(fù)雜的組合查詢(xún)而言,這將帶來(lái)極大的便利。 判定節(jié)點(diǎn)的定義可以非常靈活,我們甚至可以使用嵌套的判定節(jié)點(diǎn)來(lái)實(shí)現(xiàn)復(fù)雜的動(dòng) 態(tài)映射,如: <isNotEmpty prepend="AND" property="name"> ( name=#name# <isNotEmpty prepend="AND" property="address"> address=#address# </isNotEmpty> ) </isNotEmpty> 這段定義規(guī)定,只有用戶(hù)提供了姓名信息時(shí),才能結(jié)合地址數(shù)據(jù)進(jìn)行查詢(xún)(如果只 提供地址數(shù)據(jù),而將姓名信息忽略,將依然被視為全檢索)。 Dynamic節(jié)點(diǎn)和判定節(jié)點(diǎn)中的prepend屬性,指明了本節(jié)點(diǎn)中定義的SQL子句在 主體SQL中出現(xiàn)時(shí)的前綴。 如: <dynamic prepend="WHERE"> <isNotEmpty prepend="AND" property="name"> (name like #name#) </isNotEmpty> <isNotEmpty prepend="AND" property="address"> (address like #address#) </isNotEmpty> </dynamic> 假設(shè)"name"屬性的值為“Erica”, "address"屬性的值為“Beijing”,則會(huì) 生成類(lèi)似下面的SQL子句(實(shí)際運(yùn)行期將生成帶占位符的PreparedStatement,之 后再為其填充數(shù)據(jù)): WHERE (name like ‘Beijing’) AND (address like ‘Beijing’) 其中WHERE 之后的語(yǔ)句是在dynamic 節(jié)點(diǎn)中所定義,因此以dynamic 節(jié)點(diǎn)的 prepend設(shè)置("WHERE")作為前綴,而其中的”AND”,實(shí)際上是address屬性所對(duì) 應(yīng)的isNotEmpty節(jié)點(diǎn)的prepend設(shè)定,它引領(lǐng)了對(duì)應(yīng)節(jié)點(diǎn)中定義的SQL子句。至于 name屬性對(duì)應(yīng)的isNotEmpty節(jié)點(diǎn),由于ibatis會(huì)自動(dòng)判定是否需要追加prepend 前綴,這里(name like #name#)是WHERE 子句中的第一個(gè)條件子句,無(wú)需AND 前 綴,所以自動(dòng)省略。 判定節(jié)點(diǎn)并非僅限于isNotEmpty,ibatis中提供了豐富的判定定義功能。 判定節(jié)點(diǎn)分兩類(lèi): IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? Ø 一元判定 一元判定是針對(duì)屬性值本身的判定,如屬性是否為NULL,是否為空值等。 上面示例中isNotEmpty就是典型的一元判定。 一元判定節(jié)點(diǎn)有: 節(jié)點(diǎn)名 描述 <isPropertyAvailable> 參數(shù)類(lèi)中是否提供了此屬性 <isNotPropertyAvailable> 與<isPropertyAvailable>相反 <isNull> 屬性值是否為NULL <isNotNull> 與<isNull>相反 <isEmpty> 如果屬性為Collection或者String,其size是否<1, 如果非以上兩種類(lèi)型,則通過(guò) String.valueOf(屬性值) 獲得其String類(lèi)型的值后,判斷其size是否<1 <isNotEmpty> 與<isEmpty>相反。 Ø 二元判定 二元判定有兩個(gè)判定參數(shù),一是屬性名,而是判定值,如 <isGreaterThan prepend="AND" property="age" compareValue="18"> (age=#age#) </isGreaterThan> 其中,property="age"指定了屬性名”age”,compareValue=”18”指明 了判定值為”18”。 上面判定節(jié)點(diǎn)isGreaterThan 對(duì)應(yīng)的語(yǔ)義是:如果age 屬性大于 18(compareValue),則在SQL中加入(age=#age#)條件。 二元判定節(jié)點(diǎn)有: 節(jié)點(diǎn)名 屬性值與compareValues的關(guān)系 <isEqual> 相等。 <isNotEqual> 不等。 <isGreaterThan> 大于 <isGreaterEqual> 大于等于 <isLessThan> 小于 <isLessEqual> 小于等于 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 事務(wù)管理 基于JDBC的事務(wù)管理機(jī)制 ibatis提供了自動(dòng)化的JDBC事務(wù)管理機(jī)制。 對(duì)于傳統(tǒng)JDBC Connection 而言,我們獲取Connection 實(shí)例之后,需要調(diào)用 Connection.setAutoCommit設(shè)定事務(wù)提交模式。 在AutoCommit為true的情況下,JDBC會(huì)對(duì)我們的操作進(jìn)行自動(dòng)提交,此時(shí),每 個(gè)JDBC操作都是一個(gè)獨(dú)立的任務(wù)。 為了實(shí)現(xiàn)整體事務(wù)的原子性,我們需要將AutoCommit 設(shè)為false,并結(jié)合 Connection.commit/rollback方法進(jìn)行事務(wù)的提交/回滾操作。 ibatis 的所謂“自動(dòng)化的事務(wù)提交機(jī)制”,即ibatis 會(huì)根據(jù)當(dāng)前的調(diào)用環(huán)境,自動(dòng) 判斷操作是否需要自動(dòng)提交。 如果代碼沒(méi)有顯式的調(diào)用SqlMapClient.startTransaction()方法,則ibatis 會(huì)將當(dāng)前的數(shù)據(jù)庫(kù)操作視為自動(dòng)提交模式(AutoCommit=true),如: sqlMap = xmlBuilder.buildSqlMap(reader); User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(0)); sqlMap.update("User.updateUser", user); User user2 = new User(); user2.setId(new Integer(2)); user2.setName("Kevin"); user2.setSex(new Integer(1)); sqlMap.update("User.updateUser", user2); 在執(zhí)行sqlMap.update的時(shí)候,ibatis會(huì)自動(dòng)判定當(dāng)前的運(yùn)行環(huán)境,這里update操作 并沒(méi)有相對(duì)應(yīng)的事務(wù)范圍(startTransaction和endTransaction代碼塊),于是 ibatis 將其作為一個(gè)單獨(dú)的事務(wù),并自動(dòng)提交。對(duì)于上面的代碼,update 執(zhí)行了兩次, 與其相對(duì)應(yīng),事務(wù)也提交了兩次(即每個(gè)update操作為一個(gè)單獨(dú)的事務(wù))。 不過(guò),值得注意的是,這里的所謂“自動(dòng)判定”,可能有些誤導(dǎo),ibatis 并沒(méi)有去 檢查當(dāng)前是否已經(jīng)有事務(wù)開(kāi)啟,從而判斷當(dāng)前數(shù)據(jù)庫(kù)連接是否設(shè)定為自動(dòng)提交。 實(shí)際上,在執(zhí)行update語(yǔ)句時(shí),sqlMap會(huì)檢查當(dāng)前的Session是否已經(jīng)關(guān)聯(lián)了某個(gè) 數(shù)據(jù)庫(kù)連接,如果沒(méi)有,則取一個(gè)數(shù)據(jù)庫(kù)連接,將其AutoCommit屬性設(shè)為true,然后 執(zhí)行update 操作,執(zhí)行完之后又將這個(gè)連接釋放。這樣,上面兩次update 操作實(shí)際上 先后獲取了兩個(gè)數(shù)據(jù)庫(kù)連接,而不是我們通常所認(rèn)為的兩次update 操作都基于同一個(gè) JDBC Connection。這點(diǎn)在開(kāi)發(fā)時(shí)需特別注意。 對(duì)于多條SQL 組合而成的一個(gè)JDBC 事務(wù)操作而言,必須使用 startTransaction、commit和endTransaction操作以實(shí)現(xiàn)整體事務(wù)的原子性。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 如: try{ sqlMap = xmlBuilder.buildSqlMap(reader); sqlMap.startTransaction(); User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(0)); sqlMap.update("User.updateUser", user); User user2 = new User(); user2.setId(new Integer(2)); user2.setName("Kevin"); user2.setSex(new Integer(1)); sqlMap.update("User.updateUser", user2); sqlMap.commitTransaction(); }finally{ sqlMap.endTransaction(); } 如果user1 或者user2的update操作失敗,整個(gè)事務(wù)就會(huì)在endTransaction時(shí)回 滾,從而保證了兩次update操作的原子性。 基于JTA的事務(wù)管理機(jī)制 JTA提供了跨數(shù)據(jù)庫(kù)連接(或其他JTA資源)的事務(wù)管理能力。這一點(diǎn)是與JDBC Transaction最大的差異。 JDBC事務(wù)由Connnection管理,也就是說(shuō),事務(wù)管理實(shí)際上是在JDBC Connection 中實(shí)現(xiàn)。事務(wù)周期限于Connection的生命周期。同樣,對(duì)于基于JDBC的ibatis事務(wù)管 理機(jī)制而言,事務(wù)管理在SqlMapClient所依托的JDBC Connection中實(shí)現(xiàn),事務(wù)周期限 于SqlMapClient 的生命周期。 JTA事務(wù)管理則由 JTA容器實(shí)現(xiàn),JTA容器對(duì)當(dāng)前加入事務(wù)的眾多Connection進(jìn) 行調(diào)度,實(shí)現(xiàn)其事務(wù)性要求。JTA的事務(wù)周期可橫跨多個(gè)JDBC Connection生命周期。 同樣,對(duì)于基于JTA事務(wù)的ibatis而言,JTA事務(wù)橫跨可橫跨多個(gè)SqlMapClient。 下面這幅圖形象的說(shuō)明了這個(gè)問(wèn)題: IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 為了在ibatis中使用JTA事務(wù)管理,我們需要在配置文件中加以設(shè)定: <transactionManager type="JTA"> …… </transactionManager> 在實(shí)際開(kāi)發(fā)中,我們可能需要面對(duì)分布式事務(wù)的處理,如系統(tǒng)范圍內(nèi)包含了多個(gè)數(shù)據(jù)庫(kù), 也許還引入了JMS 上的事務(wù)管理(這在EAI 系統(tǒng)實(shí)現(xiàn)中非常常見(jiàn))。我們就需要引入JTA 以實(shí)現(xiàn)系統(tǒng)范圍內(nèi)的全局事務(wù),如下面示例中,我們同時(shí)將user 對(duì)象更新到兩個(gè)不同的數(shù) 據(jù)庫(kù): User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(0)); sqlMap1 = xmlBuilder.buildSqlMap(db1Config); sqlMap2 = xmlBuilder.buildSqlMap(db2Config); try{ sqlMap1.startTransaction(); sqlMap1.update("User.updateUser", user); sqlMap2.update("User.updateUser", user); sqlMap1. commitTransaction(); }finally{ sqlMap1.endTransaction(); } 上面的代碼中,兩個(gè)針對(duì)不同數(shù)據(jù)庫(kù)的SqlMapClient 實(shí)例,在同一個(gè)JTA 事務(wù)中 對(duì)user 對(duì)象所對(duì)應(yīng)的數(shù)據(jù)庫(kù)記錄進(jìn)行更新。外層的sqlMap1 啟動(dòng)了一個(gè)全局事務(wù),此 事務(wù)將涵蓋本線程內(nèi)commitTransaction 之前的所有數(shù)據(jù)庫(kù)操作。只要其間發(fā)生了 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 異常,則整個(gè)事務(wù)都將被回滾。 外部事務(wù)管理 基于JTA的事務(wù)管理還有另外一個(gè)特殊情況,就是利用外部事務(wù)管理機(jī)制。 對(duì)于外部事務(wù)管理,我們需要在配置文件中進(jìn)行如下設(shè)定: <transactionManager type="EXTERNAL"> …… </transactionManager> 下面是一個(gè)外部事務(wù)管理的典型示例: UserTransaction tx = new InitialContext().lookup(“……”); …… sqlMap1 = xmlBuilder.buildSqlMap(db1Config); sqlMap2 = xmlBuilder.buildSqlMap(db2Config); sqlMap1.update("User.updateUser", user); sqlMap2.update("User.updateUser", user); …… tx.commit(); 此時(shí),我們借助JTA UserTransaction實(shí)例啟動(dòng)了一個(gè)全局事務(wù)。之后的ibatis操作 (sqlMap1.update 和sqlMap2.update)全部被包含在此全局事務(wù)之中,當(dāng) UserTransaction提交的時(shí)候,ibatis操作被包含在事務(wù)中提交,反之,如果UserTransaction 回滾,那么其間的ibatis操作也將作為事務(wù)的一部分被回滾。這樣,我們就實(shí)現(xiàn)了ibatis 外部的事務(wù)控制。 另一種外部事務(wù)管理方式是借助EJB 容器,通過(guò)EJB 的部署配置,我們可以指定 EJB方法的事務(wù)性 下面是一個(gè)Session Bean的doUpdate方法,它的事務(wù)屬性被申明為“Required”, EJB容器將自動(dòng)維護(hù)此方法執(zhí)行過(guò)程中的事務(wù): /** * @ejb.interface-method * view-type="remote" * * @ejb.transaction type = "Required" **/ public void doUpdate(){ //EJB環(huán)境中,通過(guò)部署配置即可實(shí)現(xiàn)事務(wù)申明,而無(wú)需顯式調(diào)用事務(wù) …… sqlMap1 = xmlBuilder.buildSqlMap(db1Config); sqlMap2 = xmlBuilder.buildSqlMap(db2Config); sqlMap1.update("User.updateUser", user); sqlMap2.update("User.updateUser", user); …… }//方法結(jié)束時(shí),如果沒(méi)有異常發(fā)生,則事務(wù)由EJB容器自動(dòng)提交。 上面的示例中,ibatis數(shù)據(jù)操作的事務(wù)管理將全部委托給EJB容器管理,由EJB 容器控制其事務(wù)調(diào)度。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 在上面JTA事務(wù)管理的例子中,為了保持清晰,我們省略了startTransaction、 commitTransaction 和endTransaction 的編寫(xiě),在這種情況下,調(diào)用ibatis 的事務(wù)管理方法并非必須,不過(guò)在實(shí)際開(kāi)發(fā)時(shí),請(qǐng)酌情添加startTransaction、 commitTransaction 和endTransaction 語(yǔ)句,這樣可以獲得更好的性能(如果 省略了startTransaction、commitTransaction 和endTransaction 語(yǔ)句, ibatis將為每個(gè)數(shù)據(jù)操作獲取一個(gè)數(shù)據(jù)連接,就算引入了數(shù)據(jù)庫(kù)連接池機(jī)制,這樣的 無(wú)謂開(kāi)銷(xiāo)也應(yīng)盡量避免,具體請(qǐng)參見(jiàn)JDBC事務(wù)管理中的描述),并保持代碼風(fēng)格的統(tǒng) 一。 Cache 在特定硬件基礎(chǔ)上(同時(shí)假設(shè)系統(tǒng)不存在設(shè)計(jì)上的缺漏和糟糕低效的SQL 語(yǔ)句) Cache往往是提升系統(tǒng)性能的最關(guān)鍵因素)。 相對(duì)Hibernate 等封裝較為嚴(yán)密的ORM 實(shí)現(xiàn)而言(因?yàn)閷?duì)數(shù)據(jù)對(duì)象的操作實(shí)現(xiàn) 了較為嚴(yán)密的封裝,可以保證其作用范圍內(nèi)的緩存同步,而ibatis 提供的是半封閉 的封裝實(shí)現(xiàn),因此對(duì)緩存的操作難以做到完全的自動(dòng)化同步)。 ibatis 的緩存機(jī)制使用必須特別謹(jǐn)慎。特別是flushOnExecute 的設(shè)定(見(jiàn) “ibatis配置”一節(jié)中的相關(guān)內(nèi)容),需要考慮到所有可能引起實(shí)際數(shù)據(jù)與緩存數(shù)據(jù) 不符的操作。如本模塊中其他Statement對(duì)數(shù)據(jù)的更新,其他模塊對(duì)數(shù)據(jù)的更新,甚 至第三方系統(tǒng)對(duì)數(shù)據(jù)的更新。否則,臟數(shù)據(jù)的出現(xiàn)將為系統(tǒng)的正常運(yùn)行造成極大隱患。 如果不能完全確定數(shù)據(jù)更新操作的波及范圍,建議避免Cache的盲目使用。 結(jié)合cacheModel來(lái)看: <cacheModel id="product-cache" type ="LRU" readOnly="true" serialize="false"> </cacheModel> 可以看到,Cache有如下幾個(gè)比較重要的屬性: Ø readOnly Ø serialize Ø type readOnly readOnly值的是緩存中的數(shù)據(jù)對(duì)象是否只讀。這里的只讀并不是意味著數(shù)據(jù)對(duì)象一 旦放入緩存中就無(wú)法再對(duì)數(shù)據(jù)進(jìn)行修改。而是當(dāng)數(shù)據(jù)對(duì)象發(fā)生變化的時(shí)候,如數(shù)據(jù)對(duì) 象的某個(gè)屬性發(fā)生了變化,則此數(shù)據(jù)對(duì)象就將被從緩存中廢除,下次需要重新從數(shù)據(jù) 庫(kù)讀取數(shù)據(jù),構(gòu)造新的數(shù)據(jù)對(duì)象。 而readOnly="false"則意味著緩存中的數(shù)據(jù)對(duì)象可更新,如user 對(duì)象的name 屬性發(fā)生改變。 只讀Cache能提供更高的讀取性能,但一旦數(shù)據(jù)發(fā)生改變,則效率降低。系統(tǒng)設(shè)計(jì) 時(shí)需根據(jù)系統(tǒng)的實(shí)際情況(數(shù)據(jù)發(fā)生更新的概率有多大)來(lái)決定Cache的讀寫(xiě)策略。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? serialize 如果需要全局的數(shù)據(jù)緩存,CacheModel的serialize屬性必須被設(shè)為true。否則數(shù)據(jù) 緩存只對(duì)當(dāng)前Session(可簡(jiǎn)單理解為當(dāng)前線程)有效,局部緩存對(duì)系統(tǒng)的整體性能提 升有限。 在serialize="true"的情況下,如果有多個(gè)Session同時(shí)從Cache 中讀取某個(gè) 數(shù)據(jù)對(duì)象,Cache 將為每個(gè)Session返回一個(gè)對(duì)象的復(fù)本,也就是說(shuō),每個(gè)Session 將 得到包含相同信息的不同對(duì)象實(shí)例。因而Session 可以對(duì)其從Cache 獲得的數(shù)據(jù)進(jìn)行 存取而無(wú)需擔(dān)心多線程并發(fā)情況下的同步?jīng)_突。 Cache Type: 與hibernate類(lèi)似,ibatis通過(guò)緩沖接口的插件式實(shí)現(xiàn),提供了多種Cache的實(shí)現(xiàn)機(jī) 制可供選擇: 1. MEMORY 2. LRU 3. FIFO 4. OSCACHE MEMORY類(lèi)型Cache與WeakReference MEMORY 類(lèi)型的Cache 實(shí)現(xiàn),實(shí)際上是通過(guò)Java 對(duì)象引用進(jìn)行。ibatis 中,其實(shí)現(xiàn)類(lèi) 為com.ibatis.db.sqlmap.cache.memory.MemoryCacheController,MemoryCacheController 內(nèi)部, 使用一個(gè)HashMap來(lái)保存當(dāng)前需要緩存的數(shù)據(jù)對(duì)象的引用。 這里需要注意的是Java2中的三種對(duì)象引用關(guān)系: a SoftReference b WeakReference c PhantomReference 傳統(tǒng)的Java 對(duì)象引用,如: public void doSomeThing(){ User user = new User() …… } 當(dāng)doSomeThing方法結(jié)束時(shí),user 對(duì)象的引用丟失,其所占的內(nèi)存空間將由JVM在下 次垃圾回收時(shí)收回。如果我們將user 對(duì)象的引用保存在一個(gè)全局的HashMap中,如: Map map = new HashMap(); public void doSomeThing(){ User user = new User(); map.put("user",user); } 此時(shí),user 對(duì)象由于在map 中保存了引用,只要這個(gè)引用存在,那么JVM 永遠(yuǎn)也不會(huì) 收回user 對(duì)象所占用的內(nèi)存。 這樣的內(nèi)存管理機(jī)制相信諸位都已經(jīng)耳熟能詳,在絕大多數(shù)情況下,這幾乎是一種完美 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 的解決方案。但在某些情況下,卻有些不便。如對(duì)于這里的Cache 而言,當(dāng)user 對(duì)象使用 之后,我們希望保留其引用以供下次需要的時(shí)候可以重復(fù)使用,但又不希望這個(gè)引用長(zhǎng)期保 存,如果每個(gè)對(duì)象的引用都長(zhǎng)期保存下去的話,那隨著時(shí)間推移,有限的內(nèi)存空間將立即被 這些數(shù)據(jù)所消耗殆盡。最好的方式,莫過(guò)于有一種引用方式,可以在對(duì)象沒(méi)有被垃圾回收器 回收之前,依然能夠訪問(wèn)此對(duì)象,當(dāng)垃圾回收器啟動(dòng)時(shí),如果此對(duì)象沒(méi)有被其他對(duì)象所使用, 則按照常規(guī)對(duì)其進(jìn)行回收。 SoftReference、WeakReference、PhantomReference為上面的思路提供了有力支持。 這三種類(lèi)型的引用都屬于“非持續(xù)性引用”,也就是說(shuō),這種引用關(guān)系并非持續(xù)存在, 它們所代表的引用的生命周期與JVM 的運(yùn)行密切相關(guān),而非與傳統(tǒng)意義上的引用一樣依賴(lài) 于編碼階段的預(yù)先規(guī)劃。 先看一個(gè)SoftReference的例子: SoftReference ref; public void doSomeThing(){ User user = new User(); ref = new SoftReference(user); } public void doAnotherThing(){ User user = (User)ref.get();//通過(guò)SoftReference獲得對(duì)象引用 System.out.println(user.getName()); } 假設(shè)我們先執(zhí)行了doSomeThing 方法,產(chǎn)生了一個(gè)User 對(duì)象,并為其創(chuàng)建了一個(gè) SoftReference引用。 之后的某個(gè)時(shí)刻,我們調(diào)用了doAnotherThing方法,并通過(guò)SoftReference獲取User 對(duì) 象的引用。 此時(shí)我們是否還能取得user 對(duì)象的引用?這要看JVM 的運(yùn)行情況。對(duì)于SoftReference 而言,只有當(dāng)目前內(nèi)存不足的情況下,JVM 在垃圾回收時(shí)才會(huì)收回其包含的引用(JVM 并 不是只有當(dāng)內(nèi)存不足時(shí)才啟動(dòng)垃圾回收機(jī)制,何時(shí)進(jìn)行垃圾回收取決于各版本JVM 的垃圾 回收策略。如某這垃圾回收策略為:當(dāng)系統(tǒng)目前較為空閑,且無(wú)效對(duì)象達(dá)到一定比率時(shí)啟動(dòng) 垃圾回收機(jī)制,此時(shí)的空余內(nèi)存倒可能還比較充裕)。這里可能出現(xiàn)兩種情況,即: Ø JVM 目前還未出現(xiàn)過(guò)因內(nèi)存不足所引起的垃圾回收,user 對(duì)象的引用可以通過(guò) SoftReference從JVM Heap中收回。 Ø JVM 已經(jīng)因?yàn)閮?nèi)存不足啟動(dòng)了垃圾回收機(jī)制,SoftReference所包含的user 對(duì)象的 引用被JVM 所廢棄。此時(shí)ref.get方法將返回一個(gè)空引用(null),對(duì)于上面 的代碼而言,也就意味著這里可能拋出一個(gè)NullPointerException。 WeakReference比SoftReference在引用的維持性上來(lái)看更加微弱。無(wú)需等到內(nèi)存不足的 情況,只要JVM 啟動(dòng)了垃圾回收機(jī)制,那么WeakReference所對(duì)應(yīng)的對(duì)象就將被JVM 回收。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 也就是說(shuō),相對(duì)SoftReference而言,WeakReference 被JVM 回收的概率更大。 PhantomReference 比WeakReference 的引用維持性更弱。與WeakReference 和 SoftReference不同,PhantomReference所引用的對(duì)象幾乎無(wú)法被回收重用。它指向的對(duì)象實(shí) 際上已經(jīng)被JVM 銷(xiāo)毀(finalize方法已經(jīng)被執(zhí)行),只是暫時(shí)還沒(méi)被垃圾回收器收回而已。 PhantomReference主要用于輔助對(duì)象的銷(xiāo)毀過(guò)程,在實(shí)際應(yīng)用層研發(fā)中,幾乎不會(huì)涉及。 MEMORY類(lèi)型的Cache正是借助SoftReference、WeakReference以及通常意義上的Java Reference實(shí)現(xiàn)了對(duì)象的緩存管理。 下面是一個(gè)典型的MEMORY類(lèi)型Cache配置: <cacheModel id="user_cache" type="MEMORY"> <flushInterval hours="24"/> <flushOnExecute statement="updateUser"/> <property name="reference-type" value="WEAK" /> </cacheModel> 其中flushInterval 指定了多長(zhǎng)時(shí)間清除緩存,上例中指定每24 小時(shí)強(qiáng)行清空緩存 區(qū)的所有內(nèi)容。 reference-type屬性可以有以下幾種配置: 1. STRONG 即基于傳統(tǒng)的Java對(duì)象引用機(jī)制,除非對(duì)Cache顯式清空(如到了flushInterval 設(shè)定的時(shí)間;執(zhí)行了flushOnExecute所指定的方法;或代碼中對(duì)Cache執(zhí)行了清除 操作等),否則引用將被持續(xù)保留。 此類(lèi)型的設(shè)定適用于緩存常用的數(shù)據(jù)對(duì)象,或者當(dāng)前系統(tǒng)內(nèi)存非常充裕的情況。 2. SOFT 基于SoftReference 的緩存實(shí)現(xiàn),只有JVM 內(nèi)存不足的時(shí)候,才會(huì)對(duì)緩沖池中的數(shù) 據(jù)對(duì)象進(jìn)行回收。 此類(lèi)型的設(shè)定適用于系統(tǒng)內(nèi)存較為充裕,且系統(tǒng)并發(fā)量比較穩(wěn)定的情況。 3. WEAK 基于WeakReference 的緩存實(shí)現(xiàn),當(dāng)JVM 垃圾回收時(shí),緩存中的數(shù)據(jù)對(duì)象將被JVM 收回。 一般情況下,可以采用WEAK的MEMORY型Cache配置。 LRU型Cache 當(dāng)Cache達(dá)到預(yù)先設(shè)定的最大容量時(shí),ibatis會(huì)按照“最少使用”原則將使用頻率最少 的對(duì)象從緩沖中清除。 <cacheModel id="userCache" type="LRU"> <flushInterval hours="24"/> <flushOnExecute statement="updateUser"/> <property name="size" value="1000" /> </cacheModel> 可配置的參數(shù)有: u flushInterval 指定了多長(zhǎng)時(shí)間清除緩存,上例中指定每24小時(shí)強(qiáng)行清空緩存區(qū)的所有內(nèi)容。 u size IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? Cache的最大容量。 FIFO型Cache 先進(jìn)先出型緩存,最先放入Cache中的數(shù)據(jù)將被最先廢除??膳渲脜?shù)與LRU型相同: <cacheModel id="userCache" type="FIFO"> <flushInterval hours="24"/> <flushOnExecute statement="updateUser"/> <property name="size" value="1000" /> </cacheModel> OSCache 與上面幾種類(lèi)型的Cache不同,OSCache來(lái)自第三方組織Opensymphony。可以通過(guò)以 下網(wǎng)址獲得OSCache的最新版本(http://www./oscache/)。 在生產(chǎn)部署時(shí),建議采用OSCache,OSCache 是得到了廣泛使用的開(kāi)源Cache 實(shí)現(xiàn) (Hibernate 中也提供了對(duì)OSCache 的支持),它基于更加可靠高效的設(shè)計(jì),更重要的是, 最新版本的OSCache 已經(jīng)支持Cache 集群。如果系統(tǒng)需要部署在集群中,或者需要部署在 多機(jī)負(fù)載均衡模式的環(huán)境中以獲得性能上的優(yōu)勢(shì),那么OSCache在這里則是不二之選。 Ibatis中對(duì)于OSCache的配置相當(dāng)簡(jiǎn)單: <cacheModel id="userCache" type="OSCACHE"> <flushInterval hours="24"/> <flushOnExecute statement="updateUser"/> <property name="size" value="1000" /> </cacheModel> 之所以配置簡(jiǎn)單,原因在于,OSCache擁有自己的配置文件(oscache.properties)J。 下面是一個(gè)典型的OSCache配置文件: #是否使用內(nèi)存作為緩存空間 cache.memory=true #緩存管理事件監(jiān)聽(tīng)器,通過(guò)這個(gè)監(jiān)聽(tīng)器可以獲知當(dāng)前Cache 的運(yùn)行情況 cache.event.listeners=com.opensymphony.oscache.plugins.clustersupport.JMSBroa dcastingListener #如果使用磁盤(pán)緩存(cache.memory=false),則需要指定磁盤(pán)存儲(chǔ)接口實(shí)現(xiàn) #cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.Disk PersistenceListener # 磁盤(pán)緩存所使用的文件存儲(chǔ)路徑 # cache.path=c:\\myapp\\cache # 緩存調(diào)度算法,可選的有LRU,FIFO和無(wú)限緩存(UnlimitedCache) IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? # cache.algorithm=com.opensymphony.oscache.base.algorithm.FIFOCache # cache.algorithm=com.opensymphony.oscache.base.algorithm.UnlimitedCache cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache #內(nèi)存緩存的最大容量 cache.capacity=1000 # 是否限制磁盤(pán)緩存的容量 # cache.unlimited.disk=false # 基于JMS的集群緩存同步配置 #cache.cluster.jms.topic.factory=java:comp/env/jms/TopicConnectionFactory #cache.cluster.jms.topic.name=java:comp/env/jms/OSCacheTopic #cache.cluster.jms.node.name=node1 # 基于JAVAGROUP的集群緩存同步配置 #cache.cluster.properties=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_ ttl=32;mcast_send_buf_size=150000;mcast_recv_buf_size=80000):PING(timeout =2000;num_initial_members=3):MERGE2(min_interval=5000;max_interval=10000 ):FD_SOCK:VERIFY_SUSPECT(timeout=1500):pbcast.NAKACK(gc_lag=50;retransm it_timeout=300,600,1200,2400,4800):pbcast.STABLE(desired_avg_gossip=20000): UNICAST(timeout=5000):FRAG(frag_size=8096;down_thread=false;up_thread=fal se):pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_loc al_addr=true) #cache.cluster.multicast.ip=231.12.21.132 配置好之后,將此文件放在CLASSPATH 中,OSCache 在初始化時(shí)會(huì)自動(dòng)找到此 文件并根據(jù)其中的配置創(chuàng)建緩存實(shí)例。 IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? ibatis in Spring 這里我們重點(diǎn)探討Spring框架下的ibatis應(yīng)用,特別是在容器事務(wù)管理模式下的ibatis 應(yīng)用開(kāi)發(fā)。 關(guān)于Spring Framework,請(qǐng)參見(jiàn)筆者另一篇文檔: 《Spring 開(kāi)發(fā)指南》http://www./Spring_Dev_Guide.rar 針對(duì)ibatis,Spring配置文件如下: Ibatis-Context.xml: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www./dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"> <value>net.sourceforge.jtds.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value> </property> <property name="username"> <value>test</value> </property> <property name="password"> <value>changeit</value> </property> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value>SqlMapConfig.xml</value> </property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactio nManager"> IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? <property name="dataSource"> <ref local="dataSource"/> </property> </bean> <bean id="userDAO" class="net.xiaxin.dao.UserDAO"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="sqlMapClient"> <ref local="sqlMapClient" /> </property> </bean> <bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionPro xyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="target"> <ref local="userDAO" /> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> </beans> 可以看到: 1. sqlMapClient節(jié)點(diǎn) sqlMapClient節(jié)點(diǎn)實(shí)際上配置了一個(gè)sqlMapClient的創(chuàng)建工廠類(lèi)。 configLocation屬性配置了ibatis映射文件的名稱(chēng)。 2. transactionManager節(jié)點(diǎn) transactionManager采用了Spring中的DataSourceTransactionManager。 3. userDAO節(jié)點(diǎn) IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? 對(duì)應(yīng)的,UserDAO需要配置兩個(gè)屬性,sqlMapClient和DataSource, sqlMapClient將從指定的DataSource中獲取數(shù)據(jù)庫(kù)連接。 本例中Ibatis映射文件非常簡(jiǎn)單: sqlMapConfig.xml: <sqlMapConfig> <sqlMap resource="net/xiaxin/dao/entity/user.xml"/> </sqlMapConfig> net/xiaxin/dao/entity/user.xml <sqlMap namespace="User"> <typeAlias alias="user" type="net.xiaxin.dao.entity.User" /> <insert id="insertUser" parameterClass="user"> INSERT INTO users ( username, password) VALUES ( #username#, #password# ) </insert> </sqlMap> UserDAO.java: public class UserDAO extends SqlMapClientDaoSupport implements IUserDAO { public void insertUser(User user) { getSqlMapClientTemplate().update("insertUser", user); } } SqlMapClientDaoSupport(如果使用ibatis 1.x版本,對(duì)應(yīng)支持類(lèi)是 SqlMapDaoSupport)是Spring中面向ibatis的輔助類(lèi),它負(fù)責(zé)調(diào)度DataSource、 SqlMapClientTemplate(對(duì)應(yīng)ibatis 1.x版本是SqlMapTemplate)完成ibatis操作, 而DAO則通過(guò)對(duì)此類(lèi)進(jìn)行擴(kuò)展獲得上述功能。上面配置文件中針對(duì)UserDAO的屬性設(shè) 置部分,其中的屬性也是繼承自于這個(gè)基類(lèi)。 SqlMapClientTemplate對(duì)傳統(tǒng)SqlMapClient調(diào)用模式進(jìn)行了封裝,簡(jiǎn)化了上層訪問(wèn) 代碼。 User.java: public class User { public Integer id; public String username; IBATIS Developer’s Guide Version 1.0 September 2, 2004 So many open source projects. Why not Open your Documents? public String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } 測(cè)試代碼: InputStream is = new FileInputStream("Ibatis-Context.xml"); XmlBeanFactory factory = new XmlBeanFactory(is); IUserDAO userdao = (IUserDAO)factory.getBean("userDAOProxy"); User user = new User(); user.setUsername("Sofia"); user.setPassword("mypass"); userdao.insertUser(user); 對(duì)比前面ibatis 程序代碼,我們可以發(fā)現(xiàn)UserDAO.java 變得異常簡(jiǎn)潔,這也正是 Spring Framework為我們帶來(lái)的巨大幫助。 Spring以及其他IOC 框架對(duì)傳統(tǒng)的應(yīng)用框架進(jìn)行了顛覆性的革新,也許這樣的評(píng)價(jià)有 點(diǎn)過(guò)于煽情,但是這確是筆者第一次跑通Spring HelloWorld 時(shí)的切身感受。有興趣的 讀者可以研究一下Spring framework,enjoy it! |
|