在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文档。