Parameter Map 和 Inline Parameter如上所述,parameterMap 负责将 Java Bean 的属性映射成 statement 的参数。虽然 parameterMap 的外部形式很少使用,理解它如何工作对于理解 inline parameter 还是很有帮助。
1 <parameterMap id=”parameterMapName” [class=”com.domain.Product”]>2 <parameter property =”propertyName” [jdbcType=”VARCHAR”] [javaType=”string”] [nullValue=”NUMERIC”] [null=”-9999999”]/>3 <parameter …… />4 <parameter …… />5 </parameterMap>
括号[]是可选的属性。parameterMap 元素只要求属性 id 作为唯一标识。属性 class 是可选的但强烈推荐使用。和 parameterClass 类似,class 属性可以框架检查输入参数的类型并优化性能。
<parameter>元素
一个 parameterMap 可包含任意多的 parameter 元素。以下讨论 parameter 的各个属性。
* property
属性 property 是传给 statement 的参数对象的 Java Bean 属性名称。该名称根据需要,可 以在 statement 中多次出现(即在 SQL 语句 SET 子句中被更新的属性,也可以作为条件出现 在 WHERE 子句中)。
* jdbcType
属性 jdbcType 用于显式地指定给本属性(property)赋值的数据库字段的数据类型。对 于某些特定的操作,如果不指定字段的数据类型,某些 JDBC Driver 无法识别字段的数据类 型。一个很好的例子是 PreparedStatement.setNull(int parameterIndex, int sqlType)方法,要求 指定数据类型。如果不指定数据类型,某些 Driver 可能指定为 Types.Other 或 Types.Null。 但是,不能保证所有的 Driver 都表现一致。对于这种情况,SQL Map API 允许使用 parameterMap 子元素 parameter 的 jdbcType 属性指定数据类型。
正常情况下,只有当字段可以为 NULL 时才需要 jdbcType 属性。另一需要指定 jdbcType 属性的情况是字段类型为日期时间类型的情况。因为 Java 只有一个 Date 类型(java.util.Date), 而大多数 SQL 数据库有多个-通常至少有 3 种。因此,需要指定字段类型是 DATE 还是 DATETIME。
属性 jdbcType 可以是 JDBC Types 类中定义的任意参数的字符串值。虽然如此,还是有 某些类型不支持(即 BLOB)。本节的稍后部分会说明架构支持的数据类型。
注意!大多数 JDBC Driver 只有在字段可以为 NULL 时需要指定 jdbcType 属性。因此, 对于这些 Driver,只是在字段可以为 NULL 时才需要指定 type 属性。
注意!当使用 Oracle Driver 时,如果没有给可以为 NULL 的字段指定 jdbcType 属性, 当试图给这些字段赋值 NULL 时,会出现“Invalid column type”错误。
* nullValue
属性 nullValue 的值可以是对于 property 类型来说任意的合法值,用于指定 NULL 的替 换值。就是说,当 Java Bean 的属性值等于指定值时,相应的字段将赋值 NULL。这个特性 允许在应用中给不支持 null 的数据类型(即 int,double,float 等)赋值 null。当这些数据类 型的属性值匹配 null 值(即匹配-9999)时,NULL 将代替 null 值写入数据库。
1 <parameterMap id=”insert-product-param” class=”com.domain.Product”>2 <parameter property=”id” jdbcType=”NUMERIC” javaType=”int” nullValue=”-9999999”/>3 <parameter property=”description” jdbcType=”VARCHAR” nullValue=”NO_ENTRY”/>4 </parameterMap>5 <statement id=”insertProduct” parameterMap=”insert-product-param”>6 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);7 </statement>
注意!parameterMap 并不自动地绑定到特定的 Java 类。因此在上面的例子中,任何拥 有“id”和“description”属性的 Java Bean 对象,都可以作为 parameterMap 的输入。如果 需要将输入绑定到特定的 Java 类,可以使用 mapped-statement 的 resultClass 属性。
注意!Parameter Map 的名称(name)局部的,只在定义它的 SQL Map
Inline Parameter Map
parameterMap 的语法虽然简单,但很繁琐。还有一种更受欢迎更灵活的方法,可以大大简化定义和减少代码量。这种方法把 Java Bean 的属性名称嵌在 Mapped Statement 的定义中(即直接写在 SQL 语句中)。缺省情况下,任何没有指定 parameterMap 的 Mapped Statement都会被解析成 inline parameter(内嵌参数)。用上面的例子(即 Product)来说,就是:
1 <statement id=”insertProduct” parameterClass=”com.domain.Product”>2 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)3 values (#id#, #description#);4 </statement>
在内嵌参数中指定数据类型可以用下面的语法:
1 <statement id=”insertProduct” parameterClass=”com.domain.Product”>2 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)3 values (#id:NUMERIC#, #description:VARCHAR#);4 </statement>
在内嵌参数中指定数据类型和 NULL 的替代值可以用这样的语法:
1 <statement id=”insertProduct” parameterClass=”com.domain.Product”>2 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)3 values (#id:NUMERIC:-999999#, #description:VARCHAR:NO_ENTRY#);4 </statement>
注意!在内嵌参数中,要指定 NULL 的替代值,必须要先指定数据类型。 注意!如需要在查询时也使用 NULL 替代值,必须同时在 resultMap 中定义(如下说明)。 注意!如果您需要指定很多的数据类型和 NULL 替代值,可以使用外部的 parameterMap元素,这样会使代码更清晰。
基本类型输入参数
假如没必要写一个 Java Bean 作为参数,可以直接使用基本类型的包装类(即 String,Integer,Date 等)作为参数。
1 <statement id=”insertProduct” parameter=”java.lang.Integer”>2 select * from PRODUCT where PRD_ID = #value#3 </statement>
假设 PRD_ID 的数据类型是 NUMERIC,要调用上面的 mapped statement,可以传入一 个 java.lang.Integer 对象作为参数。Integer 对象的值将代替#value#参数。当使用基本类型包 装类代替 Java Bean 时,切记要使用#value#作为参数。Result Map(参见以下章节)也支持 使用基本类型作为结果参数。
Map类型输入参数
假如没必要写一个 Java Bean 作为参数,而要传入的参数又不只一个时,可以使用 Map类(如 HashMap,TreeMap 等)作为参数对象。例如:
1 <statement id=”insertProduct” parameterClass=”java.util.Map”>2 select * from PRODUCT3 where PRD_CAT_ID = #catId#4 and PRD_CODE = #code#5 </statement>
Result Map
在 SQL Map 框架中,Result Map 是极其重要的组件。在执行查询 Mapped Statement 时,resultMap 负责将结果集的列值映射成 Java Bean 的属性值。resultMap 的结构如下:
1 <resultMap id=”resultMapName” class=”some.domain.Class” [extends=”parent-resultMap”]>2 <result property=”propertyName” column=”COLUMN_NAME” [columnIndex=”1”] [javaType=”int”] [jdbcType=”NUMERIC”] [nullValue=”-999999”] [select=”someOtherStatement”]3 4 />5 <result ……/>6 <result ……/>7 <result ……/>8 </resultMap>
括号[]中是可选的属性。resultMap 的 id 属性是 statement 的唯一标识。ResultMap 的 class 属性用于指定 Java 类的全限定名(即包括包的名称)。该 Java 类初始化并根据定义填充 数据。extends 是可选的属性,可设定成另外一个 resultMap 的名字,并以它为基础。和在 Java 中继承一个类相似,父 resultMap 的属性将作为子 resutlMap 的一部分。父 resultMap 的属性总是加到子 resultMap 属性的前面,并且父 resultMap 必须要在子 resultMap 之前定义。父 resultMap 和子 resultMap 的 class 属性不一定要一致,它们可以没有任何关系。
resultMap 可以包括任意多的属性映射,将查询结果集的列值映射成 Java Bean 的属性。 属性的映射按它们在 resultMap 中定义的顺序进行。相关的 Java Bean 类必须符合 Java Bean 规范,每一属性都必须拥有 get/set 方法。
注意!ResultSet 的列值按它们在 resultMap 中定义的顺序读取(这特性会在某些实现得 不是很好的 JDBC Driver 中派上用场)。
esultMap 的 result 元素各个属性:
property
属性 property 的值是 mapped statement 返回结果对象的 Java Bean 属性的名称(get 方法)。
* column
属性 column 的值是 ResultSet 中字段的名称,该字段赋值给 names 属性指定的 Java Bean 属性。同一字段可以多次使用。注意,可能某些 JDBC Driver(例如,JDBC/ODBC 桥)不 允许多次读取同一字段。
* columnIndex
属性 columnIndex 是可选的,用于改善性能。属性 columnIndex 的值是 ResultSet 中用于 赋值 Java Bean 属性的字段次序号。在 99%的应用中,不太可能需要牺牲可读性来换取性能。 使用 columnIndex,某些 JDBC Driver 可以大幅提高性能,某些则没有任何效果.
* jdbcType
属性 type 用于指定 ResultSet 中用于赋值 Java Bean 属性的字段的数据库数据类型(而 不是 Java 类名)。虽然 resultMap 没有 NULL 值的问题,指定 type 属性对于映射某些类型(例 如 Date 属性)还是有用的。因为 Java 只有一个 Date 类型,而 SQL 数据库可能有几个(通 常至少有 3 个),为保证 Date(和其他)类型能正确的赋值,某些情况下指定 type 还是有必 要的。同样地,String 类型的赋值可能来自 VARCHAR,CHAR 和 CLOB,因此同样也有必 要指定 type 属性(取决于 JDBC Driver)。
* javaType
属性 javaType 用于显式地指定被赋值的 Java Bean 属性的类型。正常情况下,这可以通 过反射从 Java Bean 的属性获得,但对于某些映射(例如 Map 和
* nullValue
属性 nullValue 指定数据库中 NULL 的替代值。因此,如果从 ResultSet 中读出 NULL值,Java Bean 属性将被赋值属性 null 指定的替代值。属性 null 的值可以指定任意值,但必 须对于 Java Bean 属性的类型是合法的。
* select
属性 select 用于描述对象之间的关系,并自动地装入复杂类型(即用户定义的类型)属 性的数据。属性 select 的值必须是另外一个 mapped statement 元素的名称。在同一个 result 元素中定义的数据库字段(column 属性)以及 property 属性,将被传给相关的 mapped statement 作为参数。因此,字段的数据类型必须是 SQL Map 支持的简单数据类型。
隐式的Result Map
假如您有简单的映射,不需要重用定义好的 resultMap,有一个快速的方法,就是通过 设定 mapped statement 的 resultClass 属性来隐式地指定 result map。诀窍在于,保证返回的 ResultSet 的字段名称(或标签或别名)和 Java Bean 中可写入属性的名称匹配。例如,考虑 以上的 Product 类,可以创建一个带有隐式 result map 的 mapped statement 如下:
1 <statement id=”getProduct” resultClass=”com.ibatis.example.Product”>2 select3 PRD_ID as id,4 PRD_DESCRIPTION as description from PRODUCT5 where PRD_ID = #value#6 </statement>
上面的 mapped statement 定义了 resultClass 属性,并为每个字段指定了别名,用于匹配 Product 类的属性名称。这样就可以了,不需要 result map。缺点在于,您无法指定字段的数据类型(通常不是 NULLABLE 字段不需要),或 NULL 替代值(或<result>别的属性)。另外还要记住,数据库很少是大小写敏感的,因此隐式 result map 对大小写也不敏感。假如您 的 Java Bean 有两个属性,一个是 firstName,另一个是 firstname,数据库会把两者看作同一 个属性,因而不能使用隐式的 result map(这也可以看作是 Java Bean 设计的一个潜在问题)。 此外,使用 resultClass 的自动映射也对性能有轻微的不利影响。因为读取 ResultSetMetaData 信息会使某些 JDBC Driver 变慢。
基本类型的Result(即String,Integer,Boolean)
除了支持符合 Java Bean 规范的 Java 类,Result Map 还可以给基本类型包装类如 String, Integer,Boolean 等赋值。Result Map 还可以得到基本类型包装类的集合。基本类型可以象 Java Bean 一样映射,只是要记住一个 限制,基本类型只能有一个属性,名字可以任意取(常用“value”或“val”)。例如,如果 您要获得所有产品描述的一个列表而不是整个 Product 类,Result Map 如下:
1 <resultMap id=”get-product-result” class=”java.lang.String”>2 <result property=”value” column=”PRD_DESCRIPTION”/>3 </resultMap>
更简单方法是,在 mapped statement 中使用 resultClass 属性(使用“as”关键字给字段 取别名“value”):
1 <statement id=”getProductCount” resultClass=”java.lang.Integer”> 2 select count(1) as value from PRODUCT3 </statement>
Map类型的Result
Result Map 也可以方便为一个 Map(如 HashMap 或 TreeMap)对象赋值。还可以得到 Map 对象的集合(即 Map 的 List)。 Map 对象与 Java Bean 同样的方式映射,只是使用 name 属性值作为 Map 的键值,用它来索 引相应的数据库字段值,而不是象 Java Bean 一样给属性赋值。例如,如果您要将 Product 对象的数据装入 Map,可以这样做:
1 <resultMap id=”get-product-result” class=”java.util.HashMap”>2 <result property=”id” column=”PRD_ID”/>3 <result property=”code” column=”PRD_CODE”/>4 <result property=”description” column=”PRD_DESCRIPTION”/>5 <result property=”suggestedPrice” column=”PRD_SUGGESTED_PRICE”/>6 </resultMap>
上面的例子会创建一个 HashMap 的实例并用 Product 的数据赋值。Property 的 name 属性值(即“id”)作为 HashMap 的键值,而列值则作为 HashMap 中相应的值。 当然,可以把 Map 类型 Result 和隐式的 Result Map 一起使用。例如:
1 <statement id=”getProductCount” resultClass=”java.util.HashMap”>2 select * from PRODUCT3 </statement>
复杂类型属性(即自定义类型的属性)
因为 mapped statement 知道如何装入合适的数据和 Java 类,通过将 resultMap 的 property 和相应的 mapped statement 联系起来,可以自动地给复杂类型(即用户创建的类)的属性赋 值。复杂类型用以表示在数据库中相互关系为一对一,一对多的数据。对于一对多的数据关 系,拥有复杂类型属性的类作为“多”的一方,而复杂属性本身则作为“一”的一方。考虑 下面的例子:
1 <resultMap id=”get-product-result” class=”com.ibatis.example.Product”> 2 <result property=”id” column=”PRD_ID”/> 3 <result property=”description” column=”PRD_DESCRIPTION”/> 4 <result property=”category” column=”PRD_CAT_ID” select=”getCategory”/> 5 </resultMap> 6 <resultMap id=”get-category-result” class=”com.ibatis.example.Category”> 7 <result property=”id” column=”CAT_ID”/> 8 <result property=”description” column=”CAT_DESCRIPTION”/> 9 </resultMap>10 <statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>11 select * from PRODUCT where PRD_ID = #value#12 </statement>13 <statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>14 select * from CATEGORY where CAT_ID = #value#15 </statement>
上面的例子中,Product 对象拥有一个类型为 Category 的 category 属性。因为 category 是复杂类型(用户定义的类型),JDBC 不知道如何给它赋值。通过将 category 属性值和另 一个 mapped statement 联系起来,为 SQL Map 引擎如何给它赋值提供了足够的信息。通过 执行“getProduct”,“get-product-result”Result Map 使用 PRD_CAT_ID 字段的值去调用 “getCategory”。“get-category-result”Result Map 将初始化一个 Category 对象并赋值给它。 然后整个 Category 对象将赋值给 Product 的 category 属性。
以上即SQL MAP