iBATIS SQL Maps之Result Map属性进阶。

  • 隐式的Result Map

假如您有简单的映射,不需要重用定义好的resultMap,有一个快速的办法,就是通过设定mapped statement的resultClass属性来隐式地指定result map。诀窍在于,保证返回的ResultSet的字段名称(或标签或别名)和JavaBean中可写入属性的名称匹配。
例如,考虑Product类,可以创建一个带有隐式result map的 mapped statement如下:
<statement id = "getProduct" resultClass = "com.ibatis.example.Product" >
select 
PRD_ID as id,
PRD_DESCRIPTION as description
from PRODUCT
where PRD_ID = #value#
</statement>
上面的mapped statement定义了resultClass属性,并为每个字段指定了别名,用于匹配Product类的属性名称。这样就可以了,不需要result map。缺点在于,您无法指定字段的数据类型(通常不是NULLABLE字段不需要),或NULL替代值(或<result>别的属性)。另外还要记住,数据库很少是大小写敏感的,因此隐式result map对大小写也不敏感。假如您的JavaBean有两个属性,一个时firstName,另一个是firstname,数据库会把两者看做同一个属性,因而不能使用隐式的resultMap(这也可以看做是JavaBean设计的一个潜在问题)。此外,使用resultClass的自动映射也对性能有轻微的不利影响。因为读取ResultSetMetaData信息会使某些JDBC Driver变慢。

  • 基本类型的Result(即String,Integer,Boolean)

除了支持符合JavaBean规范的Java类,ResultMap还可以给基本类型包装类如String,Integer,Boolean等赋值。ResultMap还可以得到基本类型包装类的集合。基本类型可以像JavaBean一样映射,只是要记住一个限制,基本类型只能有一个属性,名字可以任意取(常用“value”或val“”)。例如,如果您要获得所有产品描述的一个列表而不是整个Product类,ResultMap如下:
<resultMap id = "get-product-result" class = "java.lang.String">
<result property = "value" column = "PRD_DESCRIPTION" />
</resultMap>
更简单方法是,在mapped Statement中使用resultClass属性(使用“as”关键字给字段取别名“value”):
<statement id = "getProductCount" resultClass = "java.lang.Integer" >
select count(1) as value
from PRODUCT
</statement>

  • Map类型的Result

ResultMap也可以方便为一个Map(如HashMap或TreeMap)对象赋值。还可以得到Map对象的集合(即Map的List)。Map对象与JavaBean同样地映射,只是使用name属性值作为Map的键值,用它来索引相应的数据库字段值,而不是像JavaBean一样给属性赋值。例如,如果您要讲Product对象的数据装入Map,可以这样做:
<resultMap id = "get-product-result" class = "java.util.HashMap" >
<result property = "id" column = "PRD_ID" />
<result property = "code" column = "PRD_CODE" / >
<result property = "description" column = "PRD_DESCRIPTION" />
<result property = "suggestedPrice" column = "PRD_SUGGESTED_PRICE" />
</resultMap>
上面的例子会创建一个HashMap的实例并用Product的数据赋值。Property的name属性值(即“id”)作为HashMap的键值,而列值则作为HashMap中相应的值。
当然,可以把Map类型Result和隐式的ResultMap一起使用。例如:
<statement id = "getProductCount" resultClass = "java.util.HashMap" >
select * from PRODUCT
</statement>
上面的例子中,结果集将以Map类型返回。

  • 复杂类型属性(即自定义类型的属性)

因为mapped statement知道如何装入合适的数据和Java类,通过将resultMap的property和相应的mapped statement联系起来,可以自动的给复杂类型(即用户创建的类)的属性赋值。复杂类型用以表示在数据库中相互关心为一对一,一对多的数据。对于一对多的数据关系,拥有复杂类型属性的类作为“多”的一方,而复杂属性本身则作为“一”的一方。考虑下面的例子:
< statement id = "getProduct" parameterClass = "int" resultMap = "get-product-result" >
select * from PRODUCT where PRD_ID = #value#
</statement>

<resultMap id = "get-product-result" class = "com.ibatis.example.Product" >
<result property = "id" column = "PRD_ID" />
<result property = "description" column = "PRD_DESCRIPTION" />
<result property = "category" column = "PRD_CAT_ID" select = "getCatagory" />
</resultMap>

<statement id = "getCatagory" parameterClass = "int" resultMap = "get-category-result" >
select * from CATEGORY where CAT_ID = #value#
</statement>

<resultMap id = "get-category-result" class = "com.ibatis.example.Category" >
<result property = "id" column = "CAT_ID"  />
<result property = "description" column = "CAT_DESCRIPTION" />
</resultMap>
上面的例子中,Product对象拥有一个类型为Category的category属性。因为category是复杂类型(用户定义的类型),JDBC不知道如何给它赋值。通过将category属性值和另一个mapped statement联系起来,为SQL Map引擎如何给它赋值提供了足够的信息。通过执行“getProduct”、“get-product-result” ResultMap使用PRD_CAT_ID字段的值去调用“getCategory”。“get-category-result” ResultMap将初始化一个Category对象并赋值给它。
然后整个Category对象将赋值给Product的category属性。
  • 避免N+1 Select(1:1)
上面的方法存在一个问题。就是无论何时加载一个Product,实际上都要执行两个SQL语句(分别加载Product和Category)。只加载一个Product的情况下,这个问题似乎微不足道。但在执行一个获得10个Product的查询时,每得到一个Product都要分别执行一个加载Category的SQL语句。结果共执行了11次查询:一次用于得到一个Product List,每得到一个Product对象都要执行另外一次查询,以获得相应的Category对象(N+1,这个例子是10+1=11)。
解决方法是,使用一个联合查询和嵌套的属性映射来代替两个查询Statement。上面例子的解决方案是:
<resultMap id = "get-product-result" class = "com.ibatis.example.Product" >
< result property = "id" column = "PRD_ID" />
< result property = "description" column = "PRD_DESCRIPTION" />
< result property = "category.id" column = "CAT_ID" />
< result property = "category.description" column = "CAT_DESCRIPTION" />
</resultMap>
<statement id = "getProduct" parameterClass = "int" resultMap = "get-product-result" >
select *
from PRDUCT,CATEGORY
where PRD_CAT_ID = cAT_ID
and PRD_ID = #value#
</statement>

  • 延迟加载VS联合查询(1:1)

必须要声明的是,使用联合查询的方案并不总是最好的。假如很少有必要访问相关的对象(如Product对象的Category属性),则不用联合查询加载所有的Category属性可能更快。对于牵涉到外部链接或没有索引字段的数据库设计时,更是如此。在这种情况下,使用延迟加载和字节码增强选项的子查询,可能性能会更好。基本的原则是,如果您需要访问相关的对象,则使用联合查询。否则,使用延迟加载和字节码增强选项的子查询。
如果您不知道选择哪种方法,别单。您可以随时更改选择而不会影响到Java代码。上面两个例子都得到相同的结果,并使用同样地调用方法。唯一要考虑的是,如果您要缓存查询结果,则使用子查询(而不是联合查询)来缓存查询结果。

  • 复杂类型集合的属性

ResultMap还可以装入代表复杂类型对象集合(List)的属性,用以表示在数据库中相互关系为多对多或一对多的数据。拥有集合属性的作为“一”的一方,而在集合中的对象作为“多”的一方。用来装入对象集合的mapped Statement和上面例子一样。唯一的不同是,让SQL Map架构装入复杂类型集合(List)的业务的属性必须是java.util.List或java.util.Collection类型。例如,如果Category拥有Product对象的List,mapped statement就像下满的例子(假设Category类有一个叫productList的属性,类型是java.util.List):
<statement id = "getCategory" parameterClas = "int" resultMap = "get-category-result" >
select * from CATEGORY where CAT_ID = #value#
</statement>

<resultMap id = "get-category-result" class = "com.ibatis.example.Category" >
<result property = "id" column = "CAT_ID" />
<result property = "description" column = "CAT_DESCRIPTION" />
<result property = "productList" column = "CAT_ID" select = "getProductsByCatId" />
</resultMap>

<statement id = "getProductsByCatId" parameterClass = "int" resultMap = "get-product-result" >
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>

<resultMap id = "get-product-result" class = "com.ibatis.example.Product" >
<result property = "id" column = "PRD _ID" />
<result property = "description " column = "PRD_DESCRIPTION " />
</resultMap>

  • 避免N+1 Select (1:M和M:N)

类似于上面1:1的情况,但因为可能涉及到更大量的数据,因此问题甚至会更严重。上面例子存在的问题是,加载一个Category将执行两个SQL语句(分别加载一个Category和一个相关Product的List)。只加载一个Category的情况下,这个问题似乎微不足道。但在执行一个获得10个Category的查询,每得到一个Category都要分别执行一个加载Product List的SQL语句。一次用于得到一个Category List,每得到一个Category对象都要执行另外一次查询,以获得相应的Product对象List(N+1,这个例子是10+1=11)。

  • 组合键值或多个复杂参数属性

您可能注意到在上面的例子中,resultMap的column只指定了一个键值。这意味着只有一个字段和相关的mapped statement关联。但是,还有一种语法能把多个字段传给相关的mapped Statement。当对象之间的关系存在组合键值关系,或只是简单地使用一个其他名称而不是#value#的属性时,它就能派上用场了。这种关于column属性的语法很简单,写成{param1 = column1,param2 = column2,...,paramN = paramN}。考虑下面的例子,其中PAYMENT表的主键是CustomerID和OrderID:
<resultMap id = "get-order-result" class = "com.ibatis.example.Order" >
<result property = "id" column = "ORD_ID" />
<result property = "customerId" column = "ORD_CST_ID" />
...
<result property = "payments" column = "{itemId=ORD_ID,custId=ORD_CST_ID}" />
</resultMap>
<statement id = "getOrderPayments" resultMap = "get-payment-result" >
select * from PAYMENT
where PAY_ORD_ID = #itemID#
and PAY_CST_ID = #custId#
</statement>
只要字段的次序和statement参数的次序相同,您也可以只指定字段名称。例如:
{ORD_ID,ORD_CST_ID}
但通常这样做会对性能有轻微的影响,并影响到可读性和可维护性。
重要提示!目前SQL Map架构无法自动解决resultMap之间的双向关系。这在处理“父/子”双向关系的resultMap时尤其要注意。一个简单的办法是,为其中一种情况再定义一个不装入父对象的resultMap(反之亦然)。
注意!某些JDBC Driver(如嵌入式PointBase)不支持同时打开多个ResultSet。这些Driver支持复杂类型映射,因为SQL Map引擎要求同时打开多个Result。
注意!Result Map的名称是局部的,只在定义它的SQL Map XML文件中有效。如果要在别的SQL Map XML文件引用它,需要在ResultMap名称前加上SQL Map的名称(在<sqlMap>根元素中定义的名称)作为前缀。
如果使用微软的SQL Server 2000JDBC驱动程序,为了在手工事务模式中执行多条语句,需要在数据库连接url加上SelectMethod=Cursor。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值