在IBatis框架中,利用一個映射文件來定義需要持久化的對象,在這個文件中SQL語句是最顯眼的,這個映射文件的目的就是將SQL映射到對象。下面先看一下映射文件的主要功能:
q 定義一個持久化的Java類。
q 映射VO中的變量屬性爲表字段。
q 根據SQL來持久化對象。
q 可以定義隨意使用的SQL。
q 支持各種條件語句的定義。
q 支持數據庫自動主鍵的生成。
q 支持存儲過程。
q 支持將結果映射成XML文檔。
q 支持多表關聯。
1. 一個具體映射文件的示例
下面看一個具體映射文件的示例。在這之前,表結構和VO將沿用第十一章的例11.2 AttackSolution.java,從這裏可以看出,IBatis框架的VO也是一個POJO。映射文件的文件名可以隨意指定,只要在sql_map_config.xml中的<sqlMap>元素定義中與之對應即可。
與例12.1 sql_map_config.xml的<sqlMap>元素對應的,將定義映射文件名爲fw_attacksolution_SqlMap.xml,示例見12.2,在示例12.2中將分段解釋各個應用:
例12.2:fw_attacksolution_SqlMap.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="fw_attacksolution">
映射文件的根節點是<sqlMap>。namespace是該<sqlMap>的命名空間,因爲SQL Map映射文件可以有多個,而對於SQL Map來說所有映射文件都是全局性的。這意味着在SQL Map中的標識只能是惟一的,利用namespace和標識的全限定名就可以進行區別,前提是,配置文件中的useStatementNamespaces屬性必須設置爲true。
<resultMap id="fwAttacksolutionResult"
class="struts.sample.cap11.sample2.entity.Attacksolution">
<result column="attack_event_code" property="attack_event_code" jdbcType="VARCHAR" />
<result column="attack_mean" property="attack_mean" jdbcType="VARCHAR" />
<result column="attack_action" property="attack_action" jdbcType="VARCHAR" />
</resultMap>
<resultMap>元素是最常用的元素,使用<resultMap>元素可以定義數據返回的對象。其id屬性在之後的各種帶返回對象的元素中,將作爲返回對象的標識。即,返回結果將會以calss屬性定義的對象作爲承體。本例中定義返回結果到VO對象Attacksolution。
<resultMap>元素的子元素以column屬性作爲數據庫表字段、並以property字段作爲VO變量屬性進行映射。返回結果根據字段設置VO變量屬性的值。jdbcType屬性則指定了數據庫表中字段的數據庫類型。
<!--定義一般的SQL語句,通常用來作爲動態SQL語句的指定。id屬性被用作對整個動態SQL語句的標識。 -->
<sql id="example_Where_Clause">
<!-- <dynamic>元素可以劃分出SQL語句的動態部分。動態部分可以包含任意多的條件元素。條件元素決定,是否在語句中包含其中的SQL代碼。-->
<!-- prepend屬性是動態SQL代碼的一部分,prepend屬性值會被用來覆蓋第一個條件元素。 -->
<dynamic prepend="where">
<!-- isPropertyAvailable用來作爲一個條件判斷元素,它判斷是否存在property屬性所指定的某個對象的變量屬 -->
<!-- prepend屬性指定了條件元素-->
<isPropertyAvailable prepend="and" property="AND_attack_event_code_NULL">
<!-- 若存在該變量屬性,則指定可以使用<isPropertyAvailable>元素的實體內容。-->
attack_event_code is null
</isPropertyAvailable>
<isPropertyAvailable prepend="and" property="AND_attack_event_code_NOT_NULL">
attack_event_code is not null
</isPropertyAvailable>
<isPropertyAvailable prepend="or" property="OR_attack_event_code_NOT_NULL">
attack_event_code is not null
</isPropertyAvailable>
<isPropertyAvailable prepend="and" property="AND_attack_event_code_EQUALS">
<!-- 用“#”包含起來的內容是某個對象中的變量屬性 -->
attack_event_code = #attack_event_code#
</isPropertyAvailable>
<isPropertyAvailable prepend="or" property="OR_attack_event_code_EQUALS">
<!-- 用“#”包含起來的內容是某個對象中的變量屬性 -->
attack_event_code = #attack_event_code#
</isPropertyAvailable>
</dynamic>
</sql>
以本例中最後一個<isPropertyAvailable>來說明:當某個對象存在變量屬性OR_attack_event_code_EQUALS時,將定義“or attack_event_code = ?”的條件語句,並判斷是否是第一個條件。如果是第一個條件則會以“where”來替換“or”,成爲“where attack_event_code = ?”,而“?”將以某個對象中變量屬性attack_event_code的值來替換。
說明:在本例中,OR_attack_event_code_EQUALS並不是VO對象的變量屬性,因此將會有一個新的Java類來提供該變量屬性,這在之後的介紹中可以看到,這種做法是非常靈活的。
<!-- 定義所有的查詢SQL -->
<!-- id聲明瞭該查詢的惟一標識 -->
<!-- resultMap屬性與<resultMap>元素的id屬性匹配表示結果映射到的VO,當查詢結果是多條記錄時,會返回一個集合 -->
<!-- parameterClass屬性是傳入參數的類,該類在這裏必須使用全限定名-->
<select id="selectByPrimaryKey"
resultMap="fwAttacksolutionResult"
parameterClass="struts.sample.cap11.sample2.entity.Attacksolution">
<!-- <select>元素的實體內容爲SQL語句,其中由“#”包含的內容將根據parameterClass屬性指定的對象中找到匹配的變量屬性值。 -->
select attack_event_code,
attack_mean,
attack_action
from fw_attacksolution
where attack_event_code = #attack_event_code#
</select>
本例中,該<select>元素的含義是:以帶條件的預處理SQL查詢表fw_attacksolution,條件的“?”參數將通過struts.sample.cap11.sample2.entity.Attacksolution類的attack_event_code變量屬性得到,查詢結果被返回到<resultMap>元素所指定的VO struts.sample.cap11.sample2.entity.Attacksolution中。
<select id="selectByExample"
resultMap="fwAttacksolutionResult"
parameterClass="java.util.Map">
select attack_event_code,
attack_mean,
attack_action
from fw_attacksolution
<!-- <include>子元素可以包含動態SQL到<select>元素的實體內容中去-->
<!-- 屬性refid指定被包含的動態SQL標識,該標識是全局性的-->
<include refid=" fw_attacksolution.example_Where_Clause"/>
<!-- 判斷property屬性所指定的對象變量屬性是否存在 -->
<isPropertyAvailable property="ORDER_BY_CLAUSE">
<!-- 使用“$”,包含的是變量屬性值 -->
order by $ORDER_BY_CLAUSE$
</isPropertyAvailable>
</select>
以上這部分仍然使用<select>元素來定義一句查詢SQL,不過這部分的SQL複雜一些,因爲這是一句動態SQL。在本例中需要注意的是,SQL的傳入參數被使用parameterClass屬性指定的java.util.Map來傳入,這個Map的內容必須符合Map實例put(method, value),在底層框架處理時會如同得到一個POJO的變量屬性名、變量屬性值一樣來取得這個Map中的每一行作爲參數名和參數值。
本例的含義是:當傳入參數中存在“ORDER_BY_CLAUSE”變量屬性時,就將“ORDER_BY_CLAUSE”的值替換$ORDER_BY_CLAUSE$,形成一句完整的order by語句。
說明:“$”和“#”同樣都是替換變量屬性的值,有什麼區別呢?“$”是一般的替換,用變量屬性值來替換“$”包含的內容。而“#”與“$”完全是處理方式的區別,包含在“#”中的變量屬性值會被用來在帶“?”的SQL中替換“?”,進行SQL預處理。
<delete id="deleteByPrimaryKey"
parameterClass="struts.sample.cap11.sample2.entity.Attacksolution">
delete from fw_attacksolution
where attack_event_code = #attack_event_code#
</delete>
<delete>元素也是執行一句SQL,但是它執行的是刪除功能的SQL。<delete>元素擁有id、parameterClass和parameterMap三種屬性,但是沒有resultClass和resultMap這兩個返回性質的屬性,所以<delete>元素執行後的SQL是無返回值的。
<delete id="deleteByExample"
parameterClass="java.util.Map">
delete from fw_attacksolution
<include refid="fw_attacksolution.example_Where_Clause"/>
</delete>
以上也是一個<delete>元素執行的刪除語句,惟一不同的是它有了動態SQL的特性,所以<delete>元素支持所有的動態SQL。
<insert id="insert"
parameterClass="struts.sample.cap11.sample2.entity.Attacksolution">
<!-- 要顯式地聲明數據庫中對應字段的類型 -->
insert into fw_attacksolution (attack_event_code, attack_mean,
attack_action)
values (#attack_event_code:VARCHAR#, #attack_mean:VARCHAR#,
#attack_action:VARCHAR#)
</insert>
<insert>元素執行的是新增記錄的SQL語句,它的特性和<delete>元素類似。本例中的insert語句會爲表fw_attacksolution插入一條記錄,記錄中的每個字段都是VARCHAR型,通過parameterClass屬性指定的對象來傳入變量屬性值。
<update id=" updateByPrimaryKey"
parameterClass="struts.sample.cap11.sample2.entity.Attacksolution">
update fw_attacksolution
set attack_mean = #attack_mean:VARCHAR#,
attack_action = #attack_action:VARCHAR#
where attack_event_code = #attack_event_code#
</update>
</sqlMap>
<update>元素執行的是更新記錄的SQL語句,它的特性和<delete>、<insert>元素一樣,也必須顯式地聲明所更新的數據庫表字段的類型,支持所有的動態SQL。
2. 其他條件判斷元素和屬性
IBatis框架對於動態SQL的支持不僅提供了<isPropertyAvailable>元素,還提供了其他類似的元素,如下所示:
q <isNotPropertyAvailable>元素:當不存在對象的變量屬性時,可以使用元素的實體內容。
q <isNull>元素:當對象的變量屬性爲null時可以使用元素的實體內容。
q <isNotNull>元素:當對象的變量屬性不爲null時可以使用元素的實體內容。
q <isEmpty>元素:檢查對象的變量屬性爲空時可以使用元素的實體內容。
q <isNotEmpty>元素:檢查對象的變量屬性不爲空時可以使用元素的實體內容。
以上這些元素都擁有prepend屬性和property屬性,property屬性被作爲某個對象的變量屬性。prepend屬性指定了條件元素,具體如下所示:
q <isEqual>元素:在比較對象與對象之間的變量屬性,或對象與靜態值相等的條件下,可以使用元素的實體內容。
q <isNotEqual>元素:在比較對象與對象之間的變量屬性,或對象與靜態值不相等的條件下,可以使用元素的實體內容。
q <isGreaterThan>元素:比較對象與對象之間的變量屬性,或對象與靜態值是否存在前者大於後者的情況,若存在,可以使用元素的實體內容。
q <isGreaterEqual>元素:比較對象與對象之間的變量屬性,或對象與靜態值是否存在前者大於且等於後者的情況,若存在,可以使用元素的實體內容。
q <isLessThan>元素:比較對象與對象之間的變量屬性,或對象與靜態值是否存在前者小於後者的情況,若存在,可以使用元素的實體內容。
q <isLessEqual>元素:比較對象與對象之間的變量屬性,或對象與靜態值是否存在前者小於且等於後者的情況,若存在,可以使用元素的實體內容
以上這些元素都擁有:prepend、property、compareProperty、compareValue四個屬性。prepend屬性指定了條件元素;property指定了某個對象的變量屬性;compareProperty作爲另一個對象的變量屬性與property指定的某個對象的變量屬性進行比較;compareValue作爲一個靜態值與property指定的某個對象的變量屬性進行比較。compareProperty和compareValue屬性至少使用一個。
3. <select>元素的其他屬性
<select>元素中,除了本例所提供的屬性外,還提供了resultClass、parameterMap兩個可選屬性。
q resultClass屬性明確指定了返回結果的全限定名對象。resultClass並不常用,因爲它沒有了映射數據庫字段的持久化特性。
q parameterMap屬性則指定了傳入參數的一個標識,讀者可以想象到,同樣會由<parameterMap>元素來定義一個傳入參數的對象,並用id來惟一標識它。的確如此,<parameterMap>和<resultMap>的寫法十分類似,但它不必要映射到表中字段,因爲僅僅是作爲一個傳入參數的對象沒有持久化的概念。<parameterMap>元素並不常用,parameterMap屬性也不常用,一般來說用parameterClass屬性已經足夠滿足大多數的需要了。
4.其他SQL執行元素
除了本例中所見到的執行SQL的元素外,IBatis框架還提供了其他的SQL執行元素。
q <statement>元素,通用的SQL執行元素,可以執行任何SQL語句。在上例中的所有SQL語句,都可以使用<statement>元素來替換。<statement>元素還提供了一個新的屬性xmlResultName作爲將返回結果生成一個XML文檔。
q <procedure>元素,用於執行存儲過程。<procedure>元素也提供了xmlResultName屬性來返回結果生成XML文檔。