hengheng123456789

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            297 Posts :: 68 Stories :: 144 Comments :: 0 Trackbacks
           

          ibatis 開發(fā)指南

          http://www.360doc.com/showWeb/0/0/262045.aspx

          相對HibernateApache OJB 等“一站式”ORM解決方案而言,ibatis 是一種“半自動化”的ORM實現(xiàn)。

          所謂“半自動”,可能理解上有點生澀。縱觀目前主流的ORM,無論Hibernate 還是Apache OJB,都對數(shù)據(jù)庫結(jié)構(gòu)提供了較為完整的封裝,提供了從POJO 到數(shù)據(jù)庫表的全套映射機制。程序員往往只需定義好了POJO 到數(shù)據(jù)庫表的映射關(guān)系,即可通過Hibernate或者OJB 提供的方法完成持久層操作。程序員甚至不需要對SQL 的熟練掌握,Hibernate/OJB 會根據(jù)制定的存儲邏輯,自動生成對應(yīng)的SQL 并調(diào)用JDBC 接口加以執(zhí)行。

          大多數(shù)情況下(特別是對新項目,新系統(tǒng)的開發(fā)而言),這樣的機制無往不利,大有一統(tǒng)天下的勢頭。但是,在一些特定的環(huán)境下,這種一站式的解決方案卻未必靈光。

          在筆者的系統(tǒng)咨詢工作過程中,常常遇到以下情況:

          1 系統(tǒng)的部分或全部數(shù)據(jù)來自現(xiàn)有數(shù)據(jù)庫,處于安全考慮,只對開發(fā)團隊提供幾條Select SQL(或存儲過程)以獲取所需數(shù)據(jù),具體的表結(jié)構(gòu)不予公開。

          2 開發(fā)規(guī)范中要求,所有牽涉到業(yè)務(wù)邏輯部分的數(shù)據(jù)庫操作,必須在數(shù)據(jù)庫層由存儲過程實現(xiàn)(就筆者工作所面向的金融行業(yè)而言,工商銀行、中國銀行、交通銀行,都在開發(fā)規(guī)范中嚴(yán)格指定)

          3 系統(tǒng)數(shù)據(jù)處理量巨大,性能要求極為苛刻,這往往意味著我們必須通過經(jīng)過高度優(yōu)化的SQL語句(或存儲過程)才能達到系統(tǒng)性能設(shè)計指標(biāo)。

          面對這樣的需求,再次舉起Hibernate 大刀,卻發(fā)現(xiàn)刀鋒不再銳利,甚至無法使用,奈何?恍惚之際,只好再摸出JDBC 準(zhǔn)備拼死一搏……,說得未免有些凄涼,直接使用JDBC進行數(shù)據(jù)庫操作實際上也是不錯的選擇,只是拖沓的數(shù)據(jù)庫訪問代碼,乏味的字段讀取操作令人厭煩。

          “半自動化”的ibatis,卻剛好解決了這個問題。

          這里的“半自動化”,是相對Hibernate等提供了全面的數(shù)據(jù)庫封裝機制的“全自動化”ORM 實現(xiàn)而言,“全自動”ORM 實現(xiàn)了POJO 和數(shù)據(jù)庫表之間的映射,以及SQL 的自動生成和執(zhí)行。而ibatis 的著力點,則在于POJO SQL之間的映射關(guān)系。也就是說,ibatis并不會為程序員在運行期自動生成SQL 執(zhí)行。具體的SQL 需要程序員編寫,然后通過映射配置文件,將SQL所需的參數(shù),以及返回的結(jié)果字段映射到指定POJO

          使用ibatis 提供的ORM機制,對業(yè)務(wù)邏輯實現(xiàn)人員而言,面對的是純粹的Java對象,這一層與通過Hibernate 實現(xiàn)ORM 而言基本一致,而對于具體的數(shù)據(jù)操作,Hibernate會自動生成SQL 語句,而ibatis 則要求開發(fā)者編寫具體的SQL 語句。相對Hibernate等“全自動”ORM機制而言,ibatis SQL開發(fā)的工作量和數(shù)據(jù)庫移植性上的讓步,為系統(tǒng)設(shè)計提供了更大的自由空間。作為“全自動”ORM 實現(xiàn)的一種有益補充,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.ibatis.com)。

          2. 創(chuàng)建測試數(shù)據(jù)庫,并在數(shù)據(jù)庫中創(chuàng)建一個t_user 表,其中包含三個字段:

          Ø id(int)

          Ø name(varchar)

          Ø sex(int)

          3. 為了在開發(fā)過程更加直觀,我們需要將ibatis日志打開以便觀察ibatis運作的細(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 實例配置

          一個典型的配置文件如下(具體配置項目的含義見后):

          <?xml version="1.0" encoding="UTF-8" ?>

          <!DOCTYPE sqlMapConfig

          PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"

          "http://www.ibatis.com/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 POJOPlain Ordinary Java Object

          下面是我們用作示例的一個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;

          }

          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 不同。因為需要人工編寫SQL 代碼,ibatis 的映射文件一般采用手動編寫(通過Copy/Paste,手工編寫映射文件也并沒想象中的麻煩)。

          針對上面POJO 的映射代碼如下:

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE sqlMap

          PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"

          "http://www.ibatis.com/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>

          從上面的映射文件可以看出,通過<insert><delete><update><select>四個節(jié)點,我們分別定義了針對TUser 對象的增刪改查操作。在這四個節(jié)點中,我們指定了對應(yīng)的SQL 語句,以update節(jié)點為例:

          ……

          <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,之后我們可以在代碼中通過指定操作id 來執(zhí)行此節(jié)點所定

          義的操作,如:

          sqlMap.update("updateUser",user);

          ID設(shè)定使得在一個配置文件中定義兩個同名節(jié)點成為可能(兩個update節(jié)

          點,以不同id區(qū)分)

          parameterClass

          指定了操作所需的參數(shù)類型, 此例中update 操作以

          com.ibatis.sample.User 類型的對象作為參數(shù),目標(biāo)是將提供的User

          實例更新到數(shù)據(jù)庫。

          parameterClass="user"中,user為“com.ibatis.sample.User

          類的別名,別名可通過typeAlias節(jié)點指定,如示例配置文件中的:

          <typeAlias alias="user" type="com.ibatis.sample.User"/>

          <![CDATA[……]]>

          通過<![CDATA[……]]>節(jié)點,可以避免SQL 中與XML 規(guī)范相沖突的字符對

          XML映射文件的合法性造成影響。

          執(zhí)行更新操作的SQL,這里的SQL 即實際數(shù)據(jù)庫支持的SQL 語句,將由

          ibatis填入?yún)?shù)后交給數(shù)據(jù)庫執(zhí)行。

          SQL中所需的用戶名參數(shù),“#name#”在運行期會由傳入的user對象的name

          屬性填充。

          SQL 中所需的用戶性別參數(shù)“#sex#”,將在運行期由傳入的user 對象的

          sex屬性填充。

          SQL中所需的條件參數(shù)“#id#”,將在運行期由傳入的user對象的id屬性

          填充。

          對于這個示例,ibatis在運行期會讀取id 為“updateUser”的update節(jié)點的SQL定義,并調(diào)用指定的user對象的對應(yīng)getter方法獲取屬性值,并用此屬性值,對SQL中的參數(shù)進行填充后提交數(shù)據(jù)庫執(zhí)行。

          此例對應(yīng)的應(yīng)用級代碼如下,其中演示了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)初始化完畢,開始執(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();

          }

          其中,SqlMapClientibatis運作的核心,所有操作均通過SqlMapClient實例完成。

          可以看出,對于應(yīng)用層而言,程序員面對的是傳統(tǒng)意義上的數(shù)據(jù)對象,而非JDBC中煩雜的ResultSet,這使得上層邏輯開發(fā)人員的工作量大大減輕,同時代碼更加清晰簡潔。數(shù)據(jù)庫操作在映射文件中加以定義,從而將數(shù)據(jù)存儲邏輯從上層邏輯代碼中獨立出來。而底層數(shù)據(jù)操作的SQL可配置化,使得我們可以控制最終的數(shù)據(jù)操作方式,通過SQL的優(yōu)化獲得最佳的數(shù)據(jù)庫執(zhí)行效能,這在依賴SQL自動生成的“全自動”ORM機制中是所難以實現(xiàn)的。

          ibatis配置

           

          結(jié)合上面示例中的ibatis配置文件。下面是對配置文件中各節(jié)點的說明:

          <?xml version="1.0" encoding="UTF-8" ?>

          <!DOCTYPE sqlMapConfig

          PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"

          "http://www.ibatis.com/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é)點

          參數(shù) 描述

          cacheModelsEnabled 是否啟用SqlMapClient上的緩存機制。

          建議設(shè)為"true"

          enhancementEnabled 是否針對POJO啟用字節(jié)碼增強機制以提升getter/setter的調(diào)用效能,避免使用JavaReflect所帶來的性能開銷。同時,這也為Lazy Loading帶來了極大的性能提升。

          建議設(shè)為"true"errorTracingEnabled 是否啟用錯誤日志,在開發(fā)期間建議設(shè)為"true"以方便調(diào)試lazyLoadingEnabled 是否啟用延遲加載機制,建議設(shè)為"true"

          maxRequests 最大并發(fā)請求數(shù)(Statement并發(fā)數(shù))

          maxTransactions 最大并發(fā)事務(wù)數(shù)

          maxSessions 最大Session 數(shù)。即當(dāng)前最大允許的并發(fā)

          SqlMapClient數(shù)。

          maxSessions設(shè)定必須介于

          maxTransactionsmaxRequests之間,即

          maxTransactions<maxSessions=<

          maxRequests

          useStatementNamespaces 是否使用Statement命名空間。

          這里的命名空間指的是映射文件中,sqlMap節(jié)點

          namespace屬性,如在上例中針對t_user

          表的映射文件sqlMap節(jié)點:

          <sqlMap namespace="User">

          這里,指定了此sqlMap節(jié)點下定義的操作均從

          屬于"User"命名空間。

          useStatementNamespaces="true"的情

          況下,Statement調(diào)用需追加命名空間,如:

          sqlMap.update("User.updateUser",user);

          否則直接通過Statement名稱調(diào)用即可,如:

          sqlMap.update("updateUser",user);

          但請注意此時需要保證所有映射文件中,

          Statement定義無重名。

          transactionManager節(jié)點

          transactionManager 節(jié)點定義了ibatis 的事務(wù)管理器,目前提供了以下幾

          種選擇:

          Ø JDBC

          通過傳統(tǒng)JDBC Connection.commit/rollback實現(xiàn)事務(wù)支持。

          Ø JTA

          使用容器提供的JTA服務(wù)實現(xiàn)全局事務(wù)管理。

          Ø EXTERNAL

          外部事務(wù)管理,如在EJB中使用ibatis,通過EJB的部署配置即可實現(xiàn)自動的事務(wù)管理機制。此時ibatis 將把所有事務(wù)委托給外部容器進行管理。

          此外,通過Spring 等輕量級容器實現(xiàn)事務(wù)的配置化管理也是一個不錯的選擇。關(guān)于結(jié)合容器實現(xiàn)事務(wù)管理,參見“高級特性”中的描述。

          dataSource節(jié)點

          dataSource從屬于transactionManager節(jié)點,用于設(shè)定ibatis運行期使用的DataSource屬性。

          type屬性: dataSource節(jié)點的type屬性指定了dataSource的實現(xiàn)類型。

          可選項目:

          Ø SIMPLE

          SIMPLEibatis內(nèi)置的dataSource實現(xiàn),其中實現(xiàn)了一個簡單的數(shù)據(jù)庫連接池機制, 對應(yīng)ibatis 實現(xiàn)類為

          com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory

          Ø DBCP:

          基于Apache DBCP 連接池組件實現(xiàn)的DataSource 封裝,當(dāng)無容器提供DataSource 服務(wù)時,建議使用該選項,對應(yīng)ibatis 實現(xiàn)類為

          com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory

          Ø JNDI

          使用J2EE 容器提供的DataSource 實現(xiàn),DataSource 將通過指定的JNDI Name 從容器中獲取。對應(yīng)ibatis 實現(xiàn)類為

          com.ibatis.sqlmap.engine.datasource.JndiDataSourceFacto

          ry

          dataSource的子節(jié)點說明(SIMPLE&DBCP):

          參數(shù) 描述

          JDBC.Driver JDBC 驅(qū)動。

          如:org.gjt.mm.mysql.Driver

          JDBC.ConnectionURL 數(shù)據(jù)庫URL

          如:jdbc:mysql://localhost/sample

          如果用的是SQLServer JDBC Driver,需要在url后追加SelectMethod=Cursor以獲得JDBC事務(wù)的多Statement支持。

          JDBC.Username 數(shù)據(jù)庫用戶名

          JDBC.Password 數(shù)據(jù)庫用戶密碼

          Pool.MaximumActiveConnections

          數(shù)據(jù)庫連接池可維持的最大容量。

          Pool.MaximumIdleConnections

          數(shù)據(jù)庫連接池中允許的掛起(idle)連接數(shù)。

          以上子節(jié)點適用于SIMPLE DBCP 模式,分別針對SIMPLE DBCP 模式的

          DataSource私有配置節(jié)點如下:

          SIMPLE:

          參數(shù) 描述

          Pool.MaximumCheckoutTime

          數(shù)據(jù)庫聯(lián)接池中,連接被某個任務(wù)所允許占用的最大時間,如果超過這個時間限定,連接將被強制收回。(毫秒)

          Pool.TimeToWait 當(dāng)線程試圖從連接池中獲取連接時,連接池中無可用連接可供使用,此時線程將進入等待狀態(tài),

          直到池中出現(xiàn)空閑連接。此參數(shù)設(shè)定了線程所允許等待的最長時間。(毫秒)

          Pool.PingQuery 數(shù)據(jù)庫連接狀態(tài)檢測語句。

          某些數(shù)據(jù)庫在連接在某段時間持續(xù)處于空閑狀態(tài)時會將其斷開。而連接池管理器將通過此語句檢測池中連接是否可用。

          檢測語句應(yīng)該是一個最簡化的無邏輯SQL

          如“select 1 from t_user”,如果執(zhí)行此語句成功,連接池管理器將認(rèn)為此連接處于可用狀態(tài)。

          Pool.PingEnabled 是否允許檢測連接狀態(tài)。

          Pool.PingConnectionsOl

          derThan

          對持續(xù)連接時間超過設(shè)定值(毫秒)的連接進行檢測。

          Pool.PingConnectionsNo

          tUsedFor

          對空閑超過設(shè)定值(毫秒)的連接進行檢測。

          DBCP:

          參數(shù) 描述

          Pool.MaximumWait 當(dāng)線程試圖從連接池中獲取連接時,連接池中無可用連接可供使用,此時線程將進入等待狀態(tài),

          直到池中出現(xiàn)空閑連接。此參數(shù)設(shè)定了線程所允許等待的最長時間。(毫秒)

          Pool.ValidationQuery 數(shù)據(jù)庫連接狀態(tài)檢測語句。

          某些數(shù)據(jù)庫在連接在某段時間持續(xù)處于空閑狀態(tài)時會將其斷開。而連接池管理器將通過此語句檢測池中連接是否可用。

          檢測語句應(yīng)該是一個最簡化的無邏輯SQL

          如“select 1 from t_user”,如果執(zhí)行此語句成功,連接池管理器將認(rèn)為此連接處于可用狀態(tài)。

          Pool.LogAbandoned 當(dāng)數(shù)據(jù)庫連接被廢棄時,是否打印日志。

          Pool.RemoveAbandonedTi

          meout

          數(shù)據(jù)庫連接被廢棄的最大超時時間Pool.RemoveAbandoned 當(dāng)連接空閑時間超過RemoveAbandonedTimeout時,是否將其廢棄。

          JNDI由于大部分配置是在應(yīng)用服務(wù)器中進行,因此ibatis中的配置相對簡單,下面是分別使用JDBCJTA事務(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é)點

          sqlMap 節(jié)點指定了映射文件的位置,配置中可出現(xiàn)多個sqlMap 節(jié)點,以指定項目內(nèi)所包含的所有映射文件。

          ibatis基礎(chǔ)語義

          XmlSqlMapClientBuilder

          XmlSqlMapClientBuilderibatis 2.0之后版本新引入的組件,用以替代1.x版本中的XmlSqlMapBuilder。其作用是根據(jù)配置文件創(chuàng)建SqlMapClient實例。

          SqlMapClient

          SqlMapClientibatis的核心組件,提供數(shù)據(jù)操作的基礎(chǔ)平臺。SqlMapClient可通過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中的相對路徑。XmlSqlMapClientBuilder通過接受一個Reader類型的配置文件句柄,根據(jù)配置參數(shù),創(chuàng)建SqlMapClient實例。

          SqlMapClient提供了眾多數(shù)據(jù)操作方法,下面是一些常用方法的示例,具體說明文檔請參見ibatis java doc,或者ibatis官方開發(fā)手冊。

          SqlMapClient基本操作示例

          以下示例摘自ibatis官方開發(fā)手冊,筆者對其進行了重新排版以獲得更好的閱讀效果。

          1: 數(shù)據(jù)寫入操作(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ù)查詢 (select)

          sqlMap.startTransaction();

          Integer key = new Integer (1);

          Product product = (Product)sqlMap.queryForObject

          (“getProduct”, key);

          sqlMap.commitTransaction();

          3: 在指定對象中存放查詢結(jié)果(select)

          sqlMap.startTransaction();

          Customer customer = new Customer();

          sqlMap.queryForObject(“getCust”, parameterObject, customer);

          sqlMap.queryForObject(“getAddr”, parameterObject, customer);

          sqlMap.commitTransaction();

          4: 執(zhí)行批量查詢 (select)

          sqlMap.startTransaction();

          List list = sqlMap.queryForList (“getProductList”, null);

          sqlMap.commitTransaction();

          5: 關(guān)于AutoCommit

          //沒有預(yù)先執(zhí)行startTransaction時,默認(rèn)為auto_commit模式

          int rows = sqlMap.insert (“insertProduct”, product);

          6:查詢指定范圍內(nèi)的數(shù)據(jù)

          sqlMap.startTransaction();

          List list = sqlMap.queryForList (“getProductList”, null, 0, 40);

          sqlMap.commitTransaction();

          7: 結(jié)合RowHandler進行查詢(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: 分頁查詢 (select)

          PaginatedList list =

          sqlMap.queryForPaginatedList (“getProductList”, null, 10);

          list.nextPage();

          list.previousPage();

          9: 基于Map的批量查詢 (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 映射

          相對Hibernate ORM 實現(xiàn)而言,ibatis的映射配置更為簡潔直接,下面是一個典型的配置文件。

          <!DOCTYPE sqlMap

          PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"

          "http://www.ibatis.com/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>

          可以看到,映射文件主要分為兩個部分:模塊配置和Statement配置。

          模塊配置包括:

          Ø typeAlias節(jié)點:

          定義了本映射文件中的別名,以避免過長變量值的反復(fù)書寫,此例中通過typeAlias節(jié)點為類"com.ibatis.sample.User"定義了一個別名"user",這樣在本配置文件的其他部分,需要引用"com.ibatis.sample.User"類時,只需以其別名替代即可。

          Ø cacheModel節(jié)點

          定義了本映射文件中使用的Cache機制:

          <cacheModel id="userCache" type="LRU">

          <flushInterval hours="24"/>

          <flushOnExecute statement="updateUser"/>

          <property name="size" value="1000" />

          </cacheModel>

          這里申明了一個名為"userCache"cacheModel,之后可以在

          Statement申明中對其進行引用:

          <select id="getUser"

          parameterClass="java.lang.String"

          resultClass="user"

          cacheModel="userCache"

          這表明對通過id"getUser"Select statement獲取的數(shù)據(jù),使用cacheModel "userCache"進行緩存。之后如果程序再次用此Statement進行數(shù)據(jù)查詢,即直接從緩存中讀取查詢結(jié)果,而無需再去數(shù)據(jù)庫查詢。

          cacheModel主要有下面幾個配置點:

          l flushInterval

          設(shè)定緩存有效期,如果超過此設(shè)定值,則將此CacheModel的緩存清空。

          l size

          CacheModel中最大容納的數(shù)據(jù)對象數(shù)量。

          l flushOnExecute

          指定執(zhí)行特定Statement時,將緩存清空。如updateUser操作將更新數(shù)據(jù)庫中的用戶信息,這將導(dǎo)致緩存中的數(shù)據(jù)對象與數(shù)據(jù)庫中的實際數(shù)據(jù)發(fā)生偏差,因此必須將緩存清空以避免臟數(shù)據(jù)的出現(xiàn)。

          關(guān)于Cache的深入探討,請參見“高級特性”中的相關(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ù)個與SQL Statement相關(guān)的節(jié)點,分別為:

          u statement

          u insert

          u delete

          u update

          u select

          u procedure

          其中,statement最為通用,它可以替代其余的所有節(jié)點。除statement之外的節(jié)點各自對應(yīng)了SQL中的同名操作(procedure對應(yīng)存儲過程)。

          使用statement 定義所有操作固然可以達成目標(biāo),但缺乏直觀性,建議在實際開發(fā)中根據(jù)操作目的,各自選用對應(yīng)的節(jié)點名加以申明。一方面,使得配置文件更加直觀,另一方面,也可借助DTD對節(jié)點申明進行更有針對性的檢查,以避免配置上的失誤。

          各種類型的Statement 配置節(jié)點的參數(shù)類型基本一致,區(qū)別在于數(shù)量不同。如

          insertupdatedelete節(jié)點無需返回數(shù)據(jù)類型定義(總是int)。

          主要的配置項如下:

          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ù)類。指定了參數(shù)的完整類名(包括包路徑)。

          可通過別名避免每次重復(fù)書寫冗長的類名。

          resultClass 結(jié)果類。指定結(jié)果類型的完整類名(包括包路徑)

          可通過別名避免每次重復(fù)書寫冗長的類名。

          parameterMap 參數(shù)映射,需結(jié)合parameterMap節(jié)點對映射

          關(guān)系加以定義。

          對于存儲過程之外的statement而言,建議使用parameterClass作為參數(shù)配置方式,一方面避

          免了參數(shù)映射配置工作,另一方面其性能表現(xiàn)也更加出色。

          resultMap 結(jié)果映射,需結(jié)合resultMap節(jié)點對映射關(guān)系

          加以定義。

          cacheModel statement對應(yīng)的Cache模塊。

          對于參數(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類設(shè)定為update statement的參數(shù),之后,我們即可在SQL 中通過#propertyName#POJO 的屬性進行引用。如上例中的:

          SET name=#name#, sex=#sex# WHERE id=#id#

          運行期,ibatis 將通過調(diào)用User 對象的getNamegetSex getId 方法獲得相應(yīng)的參數(shù)值,并將其作為SQL 的參數(shù)。

          如果parameterClass 中設(shè)定的是jdk 的中的簡單對象類型,如StringIntegeribatis會直接將其作為SQL中的參數(shù)值。

          我們也可以將包含了參數(shù)數(shù)據(jù)的Map對象傳遞給Statement,如:

          <update id="updateUser"

          parameterClass="java.util.Map">

          UPDATE t_user

          SET

          name=#name#,

          sex=#sex#

          WHERE id = #id#

          </update>

          這里傳入的參數(shù)就是一個Map對象,ibatis將以key name”、”sex”、”id”從中提取對應(yīng)的參數(shù)值。

          同樣的原理,我們也可以在resultMap中設(shè)定返回類型為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對象中返回。

          SQL中設(shè)定參數(shù)名時,可以同時指定參數(shù)類型,如:

          SET name=#name:VARCHAR#, sex=#sex:NUMERIC# WHERE

          id=#id:NUMERIC#

          對于返回結(jié)果而言,如果是select語句,建議也采用resultClass進行定義,如:

          <select id="getUser"

          parameterClass="java.lang.String"

          resultClass="user">

          <![CDATA[

          select

          name,

          sex

          from t_user

          where name = #name#

          ]]>

          </select>

          ibatis會自動根據(jù)select語句中的字段名,調(diào)用對應(yīng)POJO set方法設(shè)定屬性值,如上例中,ibatis會調(diào)用setNamesetSex 方法將Select語句返回的數(shù)據(jù)賦予相應(yīng)的POJO 實例。

          有些時候,數(shù)據(jù)庫表中的字段名過于晦澀,而為了使得代碼更易于理解,我們希望字段映射到POJO時,采用比較易讀的屬性名, 此時,我們可以通過Selectas 字句對字段名進行轉(zhuǎn)義,如(假設(shè)我們的書庫中對應(yīng)用戶名的字段為xingming,對應(yīng)性別的字段為xingbie):

          select

          xingming as name,

          xingbie as sex

          from t_user

          where id = #id#

          ibatis 會根據(jù)轉(zhuǎn)義后的字段名進行屬性映射(即調(diào)用POJO setName 方法而不是setXingming方法)。

          parameterMapresultMap實現(xiàn)了POJO到數(shù)據(jù)庫字段的映射配置,下面是一個例子:

          <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>

          ParameternullValue指定了如果參數(shù)為空(null)時的默認(rèn)值。

          之后我們即可在statement 申明中對其進行引用,如:

          <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>

          一般而言,對于insertupdatedeleteselect語句,優(yōu)先采用parameterClass

          resultClass

          parameterMap 使用較少,而resultMap 則大多用于嵌套查詢以及存儲過程的處理,之所以這樣,原因是由于存儲過程相對而言比較封閉(很多情況下需要調(diào)用現(xiàn)有的存儲過程,其參數(shù)命名和返回的數(shù)據(jù)字段命名往往不符合Java編程中的命名習(xí)慣,并且由于我們難以通過Select SQLas子句進行字段名轉(zhuǎn)義,無法使其自動與POJO中的屬性名相匹配)。此時,使用resultMap建立字段名和POJO屬性名之間的映射關(guān)系就顯得非常有效。另一方面,由于通過resultMap 指定了字段名和字段類型,ibatis無需再通過JDBC ResultSetMetaData 來動態(tài)獲取字段信息,在一定程度上也提升了性能表現(xiàn)。

          ibatis高級特性

           

          數(shù)據(jù)關(guān)聯(lián)

          至此,我們討論的都是針對獨立數(shù)據(jù)的操作。在實際開發(fā)中,我們常常遇到關(guān)聯(lián)數(shù)據(jù)的情況,如User 對象擁有若干Address 對象,每個Address 對象描述了對應(yīng)User 的一個聯(lián)系地址,這種情況下,我們應(yīng)該如何處理?

          通過單獨的Statement操作固然可以實現(xiàn)(通過Statement 用于讀取用戶數(shù)據(jù),再手工調(diào)用另外一個Statement 根據(jù)用戶ID 返回對應(yīng)的Address信息)。不過這樣未免失之繁瑣。下面我們就看看在ibatis 中,如何對關(guān)聯(lián)數(shù)據(jù)進行操作。

          ibatis 中,提供了Statement 嵌套支持,通過Statement 嵌套,我們即可實現(xiàn)關(guān)聯(lián)數(shù)據(jù)的操作。

          一對多關(guān)聯(lián)

          下面的例子中,我們首選讀取t_user 表中的所有用戶記錄,然后獲取每個用戶對應(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>

          對應(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());

          }

          }

          這里通過在resultMap 中定義嵌套查詢getAddressByUserId,我們實現(xiàn)了關(guān)聯(lián)數(shù)據(jù)的讀取。

          實際上,這種方式類似于前面所說的通過兩條單獨的Statement 進行關(guān)聯(lián)數(shù)據(jù)的讀取,只是將關(guān)聯(lián)關(guān)系在配置中加以描述,由ibatis自動完成關(guān)聯(lián)數(shù)據(jù)的讀取。

          需要注意的是,這里有一個潛在的性能問題,也就是所謂“n+1Select問題。

          注意上面示例運行過程中的日志輸出:

          ……

          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ù)讀取出來(目前t_user 表中有兩條測試數(shù)據(jù)),隨即,通過兩次Select 操作,從t_address 表中讀取兩個用戶所關(guān)聯(lián)的Address記錄。

          如果t_user 表中記錄較少,不會有明顯的影響,假設(shè)t_user 表中有十萬條記錄,那么這樣的操作將需要1000001 Select語句反復(fù)執(zhí)行才能獲得結(jié)果,無疑,隨著記錄的增長,這樣的開銷將無法承受。

          之所以在這里提及這個問題,目的在于引起讀者的注意,在系統(tǒng)設(shè)計中根據(jù)具體情況,采用一些規(guī)避手段(如使用存儲過程集中處理大批量關(guān)聯(lián)數(shù)據(jù)),從而避免因為這個問題而引起產(chǎn)品品質(zhì)上的缺陷。

          一對一關(guān)聯(lián)

          一對一關(guān)聯(lián)是一對多關(guān)聯(lián)的一種特例。這種情況下,如果采用上面的示例將導(dǎo)致1+1SQL的執(zhí)行。

          對于這種情況,我們可以采用一次Select兩張表的方式,避免這樣的性能開銷(假設(shè)上面示例中,每個User 只有一個對應(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

          *

          from t_user,t_address

          where t_user.id=t_address.user_id

          ]]>

          </select>

          與此同時,應(yīng)該保證User 類中包含addresszipCode兩個String型屬性。

          IBATIS Developer’s Guide Version 1.0

          September 2, 2004 So many open source projects. Why not Open your Documents?

          延遲加載

          在運行上面的例子時,通過觀察期間的日志輸出順序我們可以發(fā)現(xiàn),在我們執(zhí)行sqlMap.queryForList("User.getUsers", "")時,實際上ibatis只向數(shù)據(jù)庫發(fā)送了一條select id, name, sex from t_user SQL。而用于獲取Address記錄的SQL,只有在我們真正訪問address對象時,才開始執(zhí)行。

          這也就是所謂的延遲加載(Lazy Loading)機制。即當(dāng)真正需要數(shù)據(jù)的時候,才加載數(shù)據(jù)。延遲加載機制能為我們的系統(tǒng)性能帶來極大的提升。

          試想,如果我們只需要獲取用戶名稱和性別數(shù)據(jù),在沒有延遲加載特性的情況下,ibatis會一次將所有數(shù)據(jù)都從數(shù)據(jù)庫取回,包括用戶信息及其相關(guān)的地址數(shù)據(jù),而此時,關(guān)于地址數(shù)據(jù)的讀取操作沒有意義,也就是說,我們白白在地址數(shù)據(jù)的查詢讀取上浪費了大量的系統(tǒng)資源。延遲加載為我們妥善的處理了性能與編碼上的平衡(如果沒有延遲加載,我們?yōu)榱吮苊鉄o謂的性能開銷,只能專門為此再增加一個不讀取地址信息的用戶記錄檢索模塊,無疑增加了編碼上的工作量)。

          回憶之前“ibatis配置”中的內(nèi)容:

          <settings

          ……

          enhancementEnabled="true"

          lazyLoadingEnabled="true"

          ……

          />

          Settings 節(jié)點有兩個與延遲加載相關(guān)的屬性lazyLoadingEnabled enhancementEnabled,其中lazyLoadingEnabled設(shè)定了系統(tǒng)是否使用延遲加載機制,enhancementEnabled設(shè)定是否啟用字節(jié)碼強化機制(通過字節(jié)碼強化機制可以為Lazy Loading帶來性能方面的改進。

          為了使用延遲加載所帶來的性能優(yōu)勢,這兩項都建議設(shè)為"true"

          動態(tài)映射

          在復(fù)雜查詢過程中,我們常常需要根據(jù)用戶的選擇決定查詢條件,這里發(fā)生變化的并不只是SQL 中的參數(shù),包括Select 語句中所包括的字段和限定條件,都可能發(fā)生變化。典型情況,如在一個復(fù)雜的組合查詢頁面,我們必須根據(jù)用戶的選擇和輸入決定查詢的條件組合。

          一個典型的頁面如下:

          對于這個組合查詢頁面,根據(jù)用戶選擇填寫的內(nèi)容,我們應(yīng)為其生成不同的查詢語句。

          如用戶沒有填寫任何信息即提交查詢請求,我們應(yīng)該返回所有記錄:

          Select * from t_user

          如用戶只在頁面上填寫了姓名“Erica”,我們應(yīng)該生成類似:

          Select * from t_user where name like '%Erica%

          SQL查詢語句。

          如用戶只在頁面上填寫了地址“Beijing”,我們應(yīng)該生成類似:

          Select * from t_user where address like '%Beijing%”;

          SQL

          而如果用戶同時填寫了姓名和地址(”Erica&Beijing’),則我們應(yīng)生成類似:

          Select * from t_user where name like '%Erica%’ and address like '%Beijing%”

          SQL查詢語句。

          對于ibatis 這樣需要預(yù)先指定SQL 語句的ORM 實現(xiàn)而言,傳統(tǒng)的做法無非通過if-else 語句對輸入?yún)?shù)加以判定,然后針對用戶選擇調(diào)用不同的statement 定義。對于上面這種簡單的情況(兩種查詢條件的排列組合,共4 種情況)而言,statement 的重復(fù)定義工作已經(jīng)讓人不厭其煩,而對于動輒擁有七八個查詢條件,乃至十幾個查詢條件的排列組合而言,瑣碎反復(fù)的statement定義實在讓人不堪承受。

          考慮到這個問題,ibatis引入了動態(tài)映射機制,即在statement定義中,根據(jù)不同的查詢參數(shù),設(shè)定對應(yīng)的SQL語句。

          還是以上面的示例為例:

          <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>

          通過dynamic 節(jié)點,我們定義了一個動態(tài)的WHERE 子句。此WHERE 子句中將可能包含兩個針對name address 字段的判斷條件。而這兩個字段是否加入檢索取決于用戶所提供的查詢條件(字段是否為空[isNotEmpty])。

          對于一個典型的Web程序而言,我們通過HttpServletRequest獲得表單中的字段名并將其設(shè)入查詢參數(shù),如:

          user.setName(request.getParameter("name"));

          user.setAddress(request.getParameter("address"));

          sqlMap.queryForList("User.getUsers", user);

          在執(zhí)行queryForList("User.getUsers", user)時,ibatis 即根據(jù)配置文件中設(shè)定的SQL動態(tài)生成規(guī)則,創(chuàng)建相應(yīng)的SQL語句。

          上面的示例中,我們通過判定節(jié)點isNotEmpty,指定了關(guān)于name address 屬性的動態(tài)規(guī)則:

          <isNotEmpty prepend="AND" property="name">

          (name like #name#)

          </isNotEmpty>

          這個節(jié)點對應(yīng)的語義是,如果參數(shù)類的"name"屬性非空(isNotEmpty,即非空字符串””),則在生成的SQL Where字句中包括判定條件(name like #name#),其中#name#將以參數(shù)類的name屬性值填充。

          Address屬性的判定生成與name屬性完全相同,這里就不再贅述。

          這樣,我們通過在statement 定義中引入dynamic 節(jié)點,很簡單的實現(xiàn)了SQL 判定子句的動態(tài)生成,對于復(fù)雜的組合查詢而言,這將帶來極大的便利。

          判定節(jié)點的定義可以非常靈活,我們甚至可以使用嵌套的判定節(jié)點來實現(xiàn)復(fù)雜的動態(tài)映射,如:

          <isNotEmpty prepend="AND" property="name">

          ( name=#name#

          <isNotEmpty prepend="AND" property="address">

          address=#address#

          </isNotEmpty>

          )

          </isNotEmpty>

          這段定義規(guī)定,只有用戶提供了姓名信息時,才能結(jié)合地址數(shù)據(jù)進行查詢(如果只提供地址數(shù)據(jù),而將姓名信息忽略,將依然被視為全檢索)。

          Dynamic節(jié)點和判定節(jié)點中的prepend屬性,指明了本節(jié)點中定義的SQL子句在主體SQL中出現(xiàn)時的前綴。

          如:

          <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”,則會生成類似下面的SQL子句(實際運行期將生成帶占位符的PreparedStatement,之后再為其填充數(shù)據(jù)):

          WHERE (name like 'Beijing’) AND (address like 'Beijing’)

          其中WHERE 之后的語句是在dynamic 節(jié)點中所定義,因此以dynamic 節(jié)點的prepend設(shè)置("WHERE")作為前綴,而其中的”AND”,實際上是address屬性所對應(yīng)的isNotEmpty節(jié)點的prepend設(shè)定,它引領(lǐng)了對應(yīng)節(jié)點中定義的SQL子句。至于name屬性對應(yīng)的isNotEmpty節(jié)點,由于ibatis會自動判定是否需要追加prepend前綴,這里(name like #name#)WHERE 子句中的第一個條件子句,無需AND 前綴,所以自動省略。

          判定節(jié)點并非僅限于isNotEmptyibatis中提供了豐富的判定定義功能。

          判定節(jié)點分兩類:

          Ø 一元判定

          一元判定是針對屬性值本身的判定,如屬性是否為NULL,是否為空值等。

          上面示例中isNotEmpty就是典型的一元判定。

          一元判定節(jié)點有:

          節(jié)點名 描述

          <isPropertyAvailable> 參數(shù)類中是否提供了此屬性

          <isNotPropertyAvailable> <isPropertyAvailable>相反

          <isNull> 屬性值是否為NULL

          <isNotNull> <isNull>相反

          <isEmpty> 如果屬性為Collection或者String,其size是否<1,如果非以上兩種類型,則通過

          String.valueOf(屬性值)

          獲得其String類型的值后,判斷其size是否<1<isNotEmpty> <isEmpty>相反。

          Ø 二元判定

          二元判定有兩個判定參數(shù),一是屬性名,而是判定值,如

          <isGreaterThan prepend="AND" property="age"

          compareValue="18">

          (age=#age#)

          </isGreaterThan>

          其中,property="age"指定了屬性名”age”,compareValue=18”指明了判定值為”18”。

          上面判定節(jié)點isGreaterThan 對應(yīng)的語義是:如果age 屬性大于18(compareValue),則在SQL中加入(age=#age#)條件。

          二元判定節(jié)點有:

          節(jié)點名 屬性值與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ù)管理機制

          ibatis提供了自動化的JDBC事務(wù)管理機制。

          對于傳統(tǒng)JDBC Connection 而言,我們獲取Connection 實例之后,需要調(diào)用Connection.setAutoCommit設(shè)定事務(wù)提交模式。

          AutoCommittrue的情況下,JDBC會對我們的操作進行自動提交,此時,每個JDBC操作都是一個獨立的任務(wù)。

          為了實現(xiàn)整體事務(wù)的原子性,我們需要將AutoCommit 設(shè)為false,并結(jié)合Connection.commit/rollback方法進行事務(wù)的提交/回滾操作。

          ibatis 的所謂“自動化的事務(wù)提交機制”,即ibatis 會根據(jù)當(dāng)前的調(diào)用環(huán)境,自動判斷操作是否需要自動提交。

          如果代碼沒有顯式的調(diào)用SqlMapClient.startTransaction()方法,則ibatis

          會將當(dāng)前的數(shù)據(jù)庫操作視為自動提交模式(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的時候,ibatis會自動判定當(dāng)前的運行環(huán)境,這里update操作并沒有相對應(yīng)的事務(wù)范圍(startTransactionendTransaction代碼塊),于是ibatis 將其作為一個單獨的事務(wù),并自動提交。對于上面的代碼,update 執(zhí)行了兩次,與其相對應(yīng),事務(wù)也提交了兩次(即每個update操作為一個單獨的事務(wù))。

          不過,值得注意的是,這里的所謂“自動判定”,可能有些誤導(dǎo),ibatis 并沒有去檢查當(dāng)前是否已經(jīng)有事務(wù)開啟,從而判斷當(dāng)前數(shù)據(jù)庫連接是否設(shè)定為自動提交。

          實際上,在執(zhí)行update語句時,sqlMap會檢查當(dāng)前的Session是否已經(jīng)關(guān)聯(lián)了某個數(shù)據(jù)庫連接,如果沒有,則取一個數(shù)據(jù)庫連接,將其AutoCommit屬性設(shè)為true,然后執(zhí)行update 操作,執(zhí)行完之后又將這個連接釋放。這樣,上面兩次update 操作實際上先后獲取了兩個數(shù)據(jù)庫連接,而不是我們通常所認(rèn)為的兩次update 操作都基于同一個JDBC Connection。這點在開發(fā)時需特別注意。

          對于多條SQL 組合而成的一個JDBC 事務(wù)操作而言,必須使用startTransactioncommitendTransaction操作以實現(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 或者user2update操作失敗,整個事務(wù)就會在endTransaction時回滾,從而保證了兩次update操作的原子性。

          基于JTA的事務(wù)管理機制JTA提供了跨數(shù)據(jù)庫連接(或其他JTA資源)的事務(wù)管理能力。這一點是與JDBCTransaction最大的差異。

          JDBC事務(wù)由Connnection管理,也就是說,事務(wù)管理實際上是在JDBC Connection中實現(xiàn)。事務(wù)周期限于Connection的生命周期。同樣,對于基于JDBCibatis事務(wù)管理機制而言,事務(wù)管理在SqlMapClient所依托的JDBC Connection中實現(xiàn),事務(wù)周期限于SqlMapClient 的生命周期。

          JTA事務(wù)管理則由 JTA容器實現(xiàn),JTA容器對當(dāng)前加入事務(wù)的眾多Connection進行調(diào)度,實現(xiàn)其事務(wù)性要求。JTA的事務(wù)周期可橫跨多個JDBC Connection生命周期。

          同樣,對于基于JTA事務(wù)的ibatis而言,JTA事務(wù)橫跨可橫跨多個SqlMapClient

          下面這幅圖形象的說明了這個問題:

          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>

          在實際開發(fā)中,我們可能需要面對分布式事務(wù)的處理,如系統(tǒng)范圍內(nèi)包含了多個數(shù)據(jù)庫,也許還引入了JMS 上的事務(wù)管理(這在EAI 系統(tǒng)實現(xiàn)中非常常見)。我們就需要引入JTA以實現(xiàn)系統(tǒng)范圍內(nèi)的全局事務(wù),如下面示例中,我們同時將user 對象更新到兩個不同的數(shù)據(jù)庫:

          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();

          }

          上面的代碼中,兩個針對不同數(shù)據(jù)庫的SqlMapClient 實例,在同一個JTA 事務(wù)中對user 對象所對應(yīng)的數(shù)據(jù)庫記錄進行更新。外層的sqlMap1 啟動了一個全局事務(wù),此事務(wù)將涵蓋本線程內(nèi)commitTransaction 之前的所有數(shù)據(jù)庫操作。只要其間發(fā)生了IBATIS Developer’s

          異常,則整個事務(wù)都將被回滾。

          外部事務(wù)管理

          基于JTA的事務(wù)管理還有另外一個特殊情況,就是利用外部事務(wù)管理機制。

          對于外部事務(wù)管理,我們需要在配置文件中進行如下設(shè)定:

          <transactionManager type="EXTERNAL">

          ……

          </transactionManager>

          下面是一個外部事務(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();

          此時,我們借助JTA UserTransaction實例啟動了一個全局事務(wù)。之后的ibatis操作(sqlMap1.update sqlMap2.update)全部被包含在此全局事務(wù)之中,當(dāng)UserTransaction提交的時候,ibatis操作被包含在事務(wù)中提交,反之,如果UserTransaction回滾,那么其間的ibatis操作也將作為事務(wù)的一部分被回滾。這樣,我們就實現(xiàn)了ibatis外部的事務(wù)控制。

          另一種外部事務(wù)管理方式是借助EJB 容器,通過EJB 的部署配置,我們可以指定EJB方法的事務(wù)性下面是一個Session BeandoUpdate方法,它的事務(wù)屬性被申明為“Required”,EJB容器將自動維護此方法執(zhí)行過程中的事務(wù):

          /**

          * @ejb.interface-method

          * view-type="remote"

          *

          * @ejb.transaction type = "Required"

          **/

          public void doUpdate(){

          //EJB環(huán)境中,通過部署配置即可實現(xiàn)事務(wù)申明,而無需顯式調(diào)用事務(wù)……

          sqlMap1 = xmlBuilder.buildSqlMap(db1Config);

          sqlMap2 = xmlBuilder.buildSqlMap(db2Config);

          sqlMap1.update("User.updateUser", user);

          sqlMap2.update("User.updateUser", user);

          ……

          }//方法結(jié)束時,如果沒有異常發(fā)生,則事務(wù)由EJB容器自動提交。

          上面的示例中,ibatis數(shù)據(jù)操作的事務(wù)管理將全部委托給EJB容器管理,由EJB容器控制其事務(wù)調(diào)度。

          在上面JTA事務(wù)管理的例子中,為了保持清晰,我們省略了startTransactioncommitTransaction endTransaction 的編寫,在這種情況下,調(diào)用ibatis的事務(wù)管理方法并非必須,不過在實際開發(fā)時,請酌情添加startTransactioncommitTransaction endTransaction 語句,這樣可以獲得更好的性能(如果省略了startTransactioncommitTransaction endTransaction 語句,ibatis將為每個數(shù)據(jù)操作獲取一個數(shù)據(jù)連接,就算引入了數(shù)據(jù)庫連接池機制,這樣的無謂開銷也應(yīng)盡量避免,具體請參見JDBC事務(wù)管理中的描述),并保持代碼風(fēng)格的統(tǒng)一。

          Cache

          在特定硬件基礎(chǔ)上(同時假設(shè)系統(tǒng)不存在設(shè)計上的缺漏和糟糕低效的SQL 語句)Cache往往是提升系統(tǒng)性能的最關(guān)鍵因素)。

          相對Hibernate 等封裝較為嚴(yán)密的ORM 實現(xiàn)而言(因為對數(shù)據(jù)對象的操作實現(xiàn)了較為嚴(yán)密的封裝,可以保證其作用范圍內(nèi)的緩存同步,而ibatis 提供的是半封閉的封裝實現(xiàn),因此對緩存的操作難以做到完全的自動化同步)。

          ibatis 的緩存機制使用必須特別謹(jǐn)慎。特別是flushOnExecute 的設(shè)定(見“ibatis配置”一節(jié)中的相關(guān)內(nèi)容),需要考慮到所有可能引起實際數(shù)據(jù)與緩存數(shù)據(jù)不符的操作。如本模塊中其他Statement對數(shù)據(jù)的更新,其他模塊對數(shù)據(jù)的更新,甚至第三方系統(tǒng)對數(shù)據(jù)的更新。否則,臟數(shù)據(jù)的出現(xiàn)將為系統(tǒng)的正常運行造成極大隱患。

          如果不能完全確定數(shù)據(jù)更新操作的波及范圍,建議避免Cache的盲目使用。

          結(jié)合cacheModel來看:

          <cacheModel

          id="product-cache"

          type ="LRU"

          readOnly="true"

          serialize="false">

          </cacheModel>

          可以看到,Cache有如下幾個比較重要的屬性:

          Ø readOnly

          Ø serialize

          Ø type

          readOnly

          readOnly值的是緩存中的數(shù)據(jù)對象是否只讀。這里的只讀并不是意味著數(shù)據(jù)對象一旦放入緩存中就無法再對數(shù)據(jù)進行修改。而是當(dāng)數(shù)據(jù)對象發(fā)生變化的時候,如數(shù)據(jù)對象的某個屬性發(fā)生了變化,則此數(shù)據(jù)對象就將被從緩存中廢除,下次需要重新從數(shù)據(jù)庫讀取數(shù)據(jù),構(gòu)造新的數(shù)據(jù)對象。

          readOnly="false"則意味著緩存中的數(shù)據(jù)對象可更新,如user 對象的name屬性發(fā)生改變。

          只讀Cache能提供更高的讀取性能,但一旦數(shù)據(jù)發(fā)生改變,則效率降低。系統(tǒng)設(shè)計時需根據(jù)系統(tǒng)的實際情況(數(shù)據(jù)發(fā)生更新的概率有多大)來決定Cache的讀寫策略。

          serialize

          如果需要全局的數(shù)據(jù)緩存,CacheModelserialize屬性必須被設(shè)為true。否則數(shù)據(jù)緩存只對當(dāng)前Session(可簡單理解為當(dāng)前線程)有效,局部緩存對系統(tǒng)的整體性能提升有限。

          serialize="true"的情況下,如果有多個Session同時從Cache 中讀取某個數(shù)據(jù)對象,Cache 將為每個Session返回一個對象的復(fù)本,也就是說,每個Session 將得到包含相同信息的不同對象實例。因而Session 可以對其從Cache 獲得的數(shù)據(jù)進行存取而無需擔(dān)心多線程并發(fā)情況下的同步?jīng)_突。

          Cache Type:

          hibernate類似,ibatis通過緩沖接口的插件式實現(xiàn),提供了多種Cache的實現(xiàn)機制可供選擇:

          1 MEMORY

          2 LRU

          3 FIFO

          4 OSCACHE

          MEMORY類型CacheWeakReferenceMEMORY 類型的Cache 實現(xiàn),實際上是通過Java 對象引用進行。ibatis 中,其實現(xiàn)類為com.ibatis.db.sqlmap.cache.memory.MemoryCacheControllerMemoryCacheController 內(nèi)部,

          使用一個HashMap來保存當(dāng)前需要緩存的數(shù)據(jù)對象的引用。

          這里需要注意的是Java2中的三種對象引用關(guān)系:

          a SoftReference

          b WeakReference

          c PhantomReference

          傳統(tǒng)的Java 對象引用,如:

          public void doSomeThing(){

          User user = new User()

          ……

          }

          當(dāng)doSomeThing方法結(jié)束時,user 對象的引用丟失,其所占的內(nèi)存空間將由JVM在下次垃圾回收時收回。如果我們將user 對象的引用保存在一個全局的HashMap中,如:

          Map map = new HashMap();

          public void doSomeThing(){

          User user = new User();

          map.put("user",user);

          }

          此時,user 對象由于在map 中保存了引用,只要這個引用存在,那么JVM 永遠也不會收回user 對象所占用的內(nèi)存。

          這樣的內(nèi)存管理機制相信諸位都已經(jīng)耳熟能詳,在絕大多數(shù)情況下,這幾乎是一種完美的解決方案。但在某些情況下,卻有些不便。如對于這里的Cache 而言,當(dāng)user 對象使用之后,我們希望保留其引用以供下次需要的時候可以重復(fù)使用,但又不希望這個引用長期保存,如果每個對象的引用都長期保存下去的話,那隨著時間推移,有限的內(nèi)存空間將立即被這些數(shù)據(jù)所消耗殆盡。最好的方式,莫過于有一種引用方式,可以在對象沒有被垃圾回收器回收之前,依然能夠訪問此對象,當(dāng)垃圾回收器啟動時,如果此對象沒有被其他對象所使用,則按照常規(guī)對其進行回收。

          SoftReferenceWeakReferencePhantomReference為上面的思路提供了有力支持。

          這三種類型的引用都屬于“非持續(xù)性引用”,也就是說,這種引用關(guān)系并非持續(xù)存在,它們所代表的引用的生命周期與JVM 的運行密切相關(guān),而非與傳統(tǒng)意義上的引用一樣依賴

          于編碼階段的預(yù)先規(guī)劃。

          先看一個SoftReference的例子:

          SoftReference ref;

          public void doSomeThing(){

          User user = new User();

          ref = new SoftReference(user);

          }

          public void doAnotherThing(){

          User user = (User)ref.get();//通過SoftReference獲得對象引用

          System.out.println(user.getName());

          }

          假設(shè)我們先執(zhí)行了doSomeThing 方法,產(chǎn)生了一個User 對象,并為其創(chuàng)建了一個

          SoftReference引用。

          之后的某個時刻,我們調(diào)用了doAnotherThing方法,并通過SoftReference獲取User

          象的引用。

          此時我們是否還能取得user 對象的引用?這要看JVM 的運行情況。對于SoftReference而言,只有當(dāng)目前內(nèi)存不足的情況下,JVM 在垃圾回收時才會收回其包含的引用(JVM 并不是只有當(dāng)內(nèi)存不足時才啟動垃圾回收機制,何時進行垃圾回收取決于各版本JVM 的垃圾回收策略。如某這垃圾回收策略為:當(dāng)系統(tǒng)目前較為空閑,且無效對象達到一定比率時啟動垃圾回收機制,此時的空余內(nèi)存倒可能還比較充裕)。這里可能出現(xiàn)兩種情況,即:Ø JVM 目前還未出現(xiàn)過因內(nèi)存不足所引起的垃圾回收,user 對象的引用可以通過SoftReferenceJVM Heap中收回。

          Ø JVM 已經(jīng)因為內(nèi)存不足啟動了垃圾回收機制,SoftReference所包含的user 對象的引用被JVM 所廢棄。此時ref.get方法將返回一個空引用(null),對于上面的代碼而言,也就意味著這里可能拋出一個NullPointerException

          WeakReferenceSoftReference在引用的維持性上來看更加微弱。無需等到內(nèi)存不足的情況,只要JVM 啟動了垃圾回收機制,那么WeakReference所對應(yīng)的對象就將被JVM 回收。

          也就是說,相對SoftReference而言,WeakReference JVM 回收的概率更大。

          PhantomReference WeakReference 的引用維持性更弱。與WeakReference SoftReference不同,PhantomReference所引用的對象幾乎無法被回收重用。它指向的對象實際上已經(jīng)被JVM 銷毀(finalize方法已經(jīng)被執(zhí)行),只是暫時還沒被垃圾回收器收回而已。PhantomReference主要用于輔助對象的銷毀過程,在實際應(yīng)用層研發(fā)中,幾乎不會涉及。MEMORY類型的Cache正是借助SoftReferenceWeakReference以及通常意義上的JavaReference實現(xiàn)了對象的緩存管理。

          下面是一個典型的MEMORY類型Cache配置:

          <cacheModel id="user_cache" type="MEMORY">

          <flushInterval hours="24"/>

          <flushOnExecute statement="updateUser"/>

          <property name="reference-type" value="WEAK" />

          </cacheModel>

          其中flushInterval 指定了多長時間清除緩存,上例中指定每24 小時強行清空緩存區(qū)的所有內(nèi)容。

          reference-type屬性可以有以下幾種配置:

          1. STRONG

          即基于傳統(tǒng)的Java對象引用機制,除非對Cache顯式清空(如到了flushInterval設(shè)定的時間;執(zhí)行了flushOnExecute所指定的方法;或代碼中對Cache執(zhí)行了清除操作等),否則引用將被持續(xù)保留。

          此類型的設(shè)定適用于緩存常用的數(shù)據(jù)對象,或者當(dāng)前系統(tǒng)內(nèi)存非常充裕的情況。

          2. SOFT

          基于SoftReference 的緩存實現(xiàn),只有JVM 內(nèi)存不足的時候,才會對緩沖池中的數(shù)據(jù)對象進行回收。

          此類型的設(shè)定適用于系統(tǒng)內(nèi)存較為充裕,且系統(tǒng)并發(fā)量比較穩(wěn)定的情況。

          3. WEAK

          基于WeakReference 的緩存實現(xiàn),當(dāng)JVM 垃圾回收時,緩存中的數(shù)據(jù)對象將被JVM收回。

          一般情況下,可以采用WEAKMEMORYCache配置。

          LRUCache

          當(dāng)Cache達到預(yù)先設(shè)定的最大容量時,ibatis會按照“最少使用”原則將使用頻率最少的對象從緩沖中清除。

          <cacheModel id="userCache" type="LRU">

          <flushInterval hours="24"/>

          <flushOnExecute statement="updateUser"/>

          <property name="size" value="1000" />

          </cacheModel>

          可配置的參數(shù)有:

          u flushInterval

          指定了多長時間清除緩存,上例中指定每24小時強行清空緩存區(qū)的所有內(nèi)容。

          u size

          Cache的最大容量。

          FIFOCache

          先進先出型緩存,最先放入Cache中的數(shù)據(jù)將被最先廢除。可配置參數(shù)與LRU型相同:

          <cacheModel id="userCache" type="FIFO">

          <flushInterval hours="24"/>

          <flushOnExecute statement="updateUser"/>

          <property name="size" value="1000" />

          </cacheModel>

          OSCache

          與上面幾種類型的Cache不同,OSCache來自第三方組織Opensymphony。可以通過以下網(wǎng)址獲得OSCache的最新版本(http://www.opensymphony.com/oscache/)。

          在生產(chǎn)部署時,建議采用OSCacheOSCache 是得到了廣泛使用的開源Cache 實現(xiàn)(Hibernate 中也提供了對OSCache 的支持),它基于更加可靠高效的設(shè)計,更重要的是,最新版本的OSCache 已經(jīng)支持Cache 集群。如果系統(tǒng)需要部署在集群中,或者需要部署在多機負(fù)載均衡模式的環(huán)境中以獲得性能上的優(yōu)勢,那么OSCache在這里則是不二之選。

          Ibatis中對于OSCache的配置相當(dāng)簡單:

          <cacheModel id="userCache" type="OSCACHE">

          <flushInterval hours="24"/>

          <flushOnExecute statement="updateUser"/>

          <property name="size" value="1000" />

          </cacheModel>

          之所以配置簡單,原因在于,OSCache擁有自己的配置文件(oscache.propertiesJ

          下面是一個典型的OSCache配置文件:

          #是否使用內(nèi)存作為緩存空間

          cache.memory=true

          #緩存管理事件監(jiān)聽器,通過這個監(jiān)聽器可以獲知當(dāng)前Cache 的運行情況cache.event.listeners=com.opensymphony.oscache.plugins.clustersupport.JMSBroa

          dcastingListener

          #如果使用磁盤緩存(cache.memory=false),則需要指定磁盤存儲接口實現(xiàn)#cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.Disk

          PersistenceListener

          # 磁盤緩存所使用的文件存儲路徑

          # cache.path=c:""myapp""cache

          # 緩存調(diào)度算法,可選的有LRU,FIFO和無限緩存(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

          # 是否限制磁盤緩存的容量

          # 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 在初始化時會自動找到此

          文件并根據(jù)其中的配置創(chuàng)建緩存實例。

          IBATIS Developer’s Guide Version 1.0

          September 2, 2004 So many open source projects. Why not Open your Documents?

          ibatis in Spring

          這里我們重點探討Spring框架下的ibatis應(yīng)用,特別是在容器事務(wù)管理模式下的ibatis

          應(yīng)用開發(fā)。

          關(guān)于Spring Framework,請參見筆者另一篇文檔:

          Spring 開發(fā)指南》http://www.xiaxin.net/Spring_Dev_Guide.rar

          針對ibatisSpring配置文件如下:

          Ibatis-Context.xml:

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

          "http://www.springframework.org/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é)點

          sqlMapClient節(jié)點實際上配置了一個sqlMapClient的創(chuàng)建工廠類。

          configLocation屬性配置了ibatis映射文件的名稱。

          2.    transactionManager節(jié)點

          transactionManager采用了Spring中的DataSourceTransactionManager

          3.    userDAO節(jié)點

          IBATIS Developer’s Guide Version 1.0

          September 2, 2004 So many open source projects. Why not Open your Documents?

          對應(yīng)的,UserDAO需要配置兩個屬性,sqlMapClientDataSource

          sqlMapClient將從指定的DataSource中獲取數(shù)據(jù)庫連接。

          本例中Ibatis映射文件非常簡單:

          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版本,對應(yīng)支持類是SqlMapDaoSupport)是Spring中面向ibatis的輔助類,它負(fù)責(zé)調(diào)度DataSourceSqlMapClientTemplate(對應(yīng)ibatis 1.x版本是SqlMapTemplate)完成ibatis操作,而DAO則通過對此類進行擴展獲得上述功能。上面配置文件中針對UserDAO的屬性設(shè)置部分,其中的屬性也是繼承自于這個基類。

          SqlMapClientTemplate對傳統(tǒng)SqlMapClient調(diào)用模式進行了封裝,簡化了上層訪問代碼。

          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;

          }

          }

          測試代碼:

          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);

          對比前面ibatis 程序代碼,我們可以發(fā)現(xiàn)UserDAO.java 變得異常簡潔,這也正是Spring Framework為我們帶來的巨大幫助。

          Spring以及其他IOC 框架對傳統(tǒng)的應(yīng)用框架進行了顛覆性的革新,也許這樣的評價有點過于煽情,但是這確是筆者第一次跑通Spring HelloWorld 時的切身感受。有興趣的讀者可以研究一下Spring frameworkenjoy it

          posted on 2007-09-03 15:33 哼哼 閱讀(3657) 評論(0)  編輯  收藏 所屬分類: JAVA-DB
          主站蜘蛛池模板: 平昌县| 高清| 仪征市| 天峨县| 舟曲县| 梅州市| 镇赉县| 卫辉市| 成都市| 昭苏县| 宕昌县| 西贡区| 抚州市| 全州县| 大渡口区| 阿巴嘎旗| 兰西县| 大洼县| 长春市| 新蔡县| 宁津县| 沛县| 桃园市| 英山县| 双鸭山市| 洮南市| 仁布县| 新干县| 乐都县| 象山县| 嘉黎县| 郎溪县| 泸州市| 新乐市| 赤壁市| 县级市| 温泉县| 文成县| 永吉县| 丰宁| 定边县|