Mybatis最入门---ResultMaps高级用法(下)

来源:互联网 发布:淘宝三钻店铺多少钱 编辑:程序博客网 时间:2024/06/06 20:07

接上文,本文我们继续来叙述Mybatis中resultMap的高级用法,类似的,我们先给大家叙述基本的概念及用法,具体实例在后文中再做演示,敬请期待!

-------------------------------------------------------------------------------------------------------------------------------------

上文,我们说到了“has-many”这个问题,在Mybatis中提供给我们另外一个非常有用的元素:

1.<collection>集合

下面给出一个典型的<collection>元素的示例,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <collection property="posts" ofType="domain.blog.Post">  
  2.   <id property="id" column="post_id"/>  
  3.   <result property="subject" column="post_subject"/>  
  4.   <result property="body" column="post_body"/>  
  5. </collection>  

这里的<collection>集合元素的功能,几乎和<association>元素的几乎是相同的。实际上,在设计之初也是非常类似的,这里我们就不再关注它们的相同点,而重点看看,它们的差异在哪里?

我们先看看现实场景是什么?

a.一个博客只有一个作者。

b.一个作者有很多文章。

我们先回顾一下,基本增改删查中的select,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <select id="findUserById" parameterType="String" resultType="User">    
  2.        select * from sysuser where id=#{id}    
  3. </select>    
此时,如果数据库中存在多条数据,那么我们的dao层接口就需要修改为如下的内容:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public List<User> users;  
这里类似的,一个作者有很多文章,那么结果返回的接口写法如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private List<Post> posts;  

类似于上面的,为了映射嵌套结果集合到List中,就像<association> element元素一样,我们可以使用嵌套查询,或者使用嵌套结果

2.集合的嵌套查询

首先,我们看看如何使用嵌套查询来加载blog上posts的文章

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="blogResult" type="Blog">  
  2.   <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>  
  3. </resultMap>  
  4.   
  5. <select id="selectBlog" resultMap="blogResult">  
  6.   SELECT * FROM BLOG WHERE ID = #{id}  
  7. </select>  
  8.   
  9. <select id="selectPostsForBlog" resultType="Post">  
  10.   SELECT * FROM POST WHERE BLOG_ID = #{id}  
  11. </select>  
特别的,这里有几件事情需要注意,尽管,看起来上面的这里和上面的<association>非常相似。
第一:明确我们使用的是<collection>元素

第二:这里有一个全新的属性:"ofType",这个属性是必须的。它用来区分JavaBean(或者字段)属性类型和集合包含的类型。

因此,请各位看官仔细阅读下面这条配置:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>  

翻译过来就是:一个”posts“在一个“ArrayList”类型的“Post”中的集合(collection)

在上面的这句话中,javaType并不是必须要存在的,Mybatis能够在多数情况下自动的配置到对应的类型当中,因此,这句话也可以简化为:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>  


3.集合的嵌套结果

对于这一点,各位看官大概已经能够猜到集合的嵌套结果是如何工作的了,因为,它确实和<association>一样,但同时也增加了一个相同的额外的“ofType”属性。

首先,我们先来看看SQL语句该怎么写:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <select id="selectBlog" resultMap="blogResult">  
  2.   select  
  3.   B.id as blog_id,  
  4.   B.title as blog_title,  
  5.   B.author_id as blog_author_id,  
  6.   P.id as post_id,  
  7.   P.subject as post_subject,  
  8.   P.body as post_body,  
  9.   from Blog B  
  10.   left outer join Post P on B.id = P.blog_id  
  11.   where B.id = #{id}  
  12. </select>  
我们又一次关联Blog,Post两张表。重点关注简单映射之后的结果列的标签名。现在用“posts”的集合来映射“blog”就可以简写为:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="blogResult" type="Blog">  
  2.   <id property="id" column="blog_id" />  
  3.   <result property="title" column="blog_title"/>  
  4.   <collection property="posts" ofType="Post">  
  5.     <id property="id" column="post_id"/>  
  6.     <result property="subject" column="post_subject"/>  
  7.     <result property="body" column="post_body"/>  
  8.   </collection>  
  9. </resultMap>  
同样的,请各位看官一定要记得id元素的重要性,否则的话,请查看前文中,我们对id的解释。

与此同时,如果我们选择了更长形式,来允许更加可以重用的结果集形式,我们可以参照下面的这种写法:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="blogResult" type="Blog">  
  2.   <id property="id" column="blog_id" />  
  3.   <result property="title" column="blog_title"/>  
  4.   <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/>  
  5. </resultMap>  
  6.   
  7. <resultMap id="blogPostResult" type="Post">  
  8.   <id property="id" column="id"/>  
  9.   <result property="subject" column="subject"/>  
  10.   <result property="body" column="body"/>  
  11. </resultMap>  

4.集合的多结果集

正如我们在<association>中实现的那样,我们可以执行2句查询,从而返回两个结果集合,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. SELECT * FROM BLOG WHERE ID = #{id}  
  2.   
  3. SELECT * FROM POST WHERE BLOG_ID = #{id}  

在这里,我们也必须给定一个指定的名称给对应的结果集合,做法是:增加一个“resultSets”属性,并使用逗号作为间隔。如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <select id="selectBlog" resultSets="blogs,posts" resultMap="blogResult">  
  2.   {call getBlogsAndPosts(#{id,jdbcType=INTEGER,mode=IN})}  
  3. </select>  

同样,我们也指定:数据填充的“posts”的集合包含在“posts”的结果集中

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="blogResult" type="Blog">  
  2.   <id property="id" column="id" />  
  3.   <result property="title" column="title"/>  
  4.   <collection property="posts" ofType="Post" resultSet="posts" column="id" foreignColumn="blog_id">  
  5.     <id property="id" column="id"/>  
  6.     <result property="subject" column="subject"/>  
  7.     <result property="body" column="body"/>  
  8.   </collection>  
  9. </resultMap>  
注意:

这里对于我们映射没有深度,广度,<association>与<collections>的关联,有任何的限制。我们最好能够记得这种用法的实际效果。最好的就是,我们持续不断的进行单元测试直到发现一个最好的实现途径。不过不必担心,Mybatis能够使得我们修改非常少的代码,就实现不同的效果。这是不是很赞呢?

关于映射,结合,关联是一个非常值得深入研究的方向,本文,我们就先说到这里,更多的内容还请各位看官在使用中不断发现吧!

5.<discriminator>鉴别器

先来回顾我们上文中给的示例代码,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <discriminator javaType="int" column="draft">  
  2.   <case value="1" resultType="DraftPost"/>  
  3. </discriminator>  

某些情况下,一个简单的数据库查询可能返回多个不同数据类型的,存在关联的结果集合。这个<discriminator>元素就是帮助我们来处理这种场景,或者是,包括类的继承层次结构。<discriminator>非常的容易理解,各位看官可以类比java中的switch语句。

定义一个<discriminator>需要指定“column”和“javaType”。“column”用来帮助Mybatis寻找比对结果中的值,“javaType”用来保证类型上的匹配。具体的例子,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="vehicleResult" type="Vehicle">  
  2.   <id property="id" column="id" />  
  3.   <result property="vin" column="vin"/>  
  4.   <result property="year" column="year"/>  
  5.   <result property="make" column="make"/>  
  6.   <result property="model" column="model"/>  
  7.   <result property="color" column="color"/>  
  8.   <discriminator javaType="int" column="vehicle_type">  
  9.     <case value="1" resultMap="carResult"/>  
  10.     <case value="2" resultMap="truckResult"/>  
  11.     <case value="3" resultMap="vanResult"/>  
  12.     <case value="4" resultMap="suvResult"/>  
  13.   </discriminator>  
  14. </resultMap>  
在这个例子中,Mybatis将会从结果集合中得到每一条记录。并且比较其”vehicle type“的值。如果正确的匹配到了某个case的值,它就会使用对应的”resultMap“。换句话说,这种机制完全的忽略了其他的”resultMap“(有特例,待会介绍)。但是,如果没有一个case被匹配到的话,Mybatis将会使用<discriminator>标签外部定义的”resultMap“,因此,如果”carResult“被声明为如下内容:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="carResult" type="Car">  
  2.   <result property="doorCount" column="door_count" />  
  3. </resultMap>  

在这种情况下,只有”doorCount“将会被加载。加载之后,Mybatis就允许彻底孤立的<discriminator>的case,即使没有与其parent resultMap存在任何关系。在这种情况下,我们就会知道:”cars“与”vehicles“之间没有任何的关系。就算他们之间是”is-a“的关系。因此,我们想要其余的属性也能够被加载的话,一个最简单的变化就是如下的做法:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="carResult" type="Car" extends="vehicleResult">  
  2.   <result property="doorCount" column="door_count" />  
  3. </resultMap>  

这样,”vehicleResult“和”carResult“的所有属性都会被加载。

曾经也许有人已经有人发现了外部映射的定义有点让人反感。所以,下面有一种比较简单写法,如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="vehicleResult" type="Vehicle">  
  2.   <id property="id" column="id" />  
  3.   <result property="vin" column="vin"/>  
  4.   <result property="year" column="year"/>  
  5.   <result property="make" column="make"/>  
  6.   <result property="model" column="model"/>  
  7.   <result property="color" column="color"/>  
  8.   <discriminator javaType="int" column="vehicle_type">  
  9.     <case value="1" resultType="carResult">  
  10.       <result property="doorCount" column="door_count" />  
  11.     </case>  
  12.     <case value="2" resultType="truckResult">  
  13.       <result property="boxSize" column="box_size" />  
  14.       <result property="extendedCab" column="extended_cab" />  
  15.     </case>  
  16.     <case value="3" resultType="vanResult">  
  17.       <result property="powerSlidingDoor" column="power_sliding_door" />  
  18.     </case>  
  19.     <case value="4" resultType="suvResult">  
  20.       <result property="allWheelDrive" column="all_wheel_drive" />  
  21.     </case>  
  22.   </discriminator>  
  23. </resultMap>  
注意:在上面结果集合中,Mybatis能够自动的帮助我们映射对象的属性与列。因此,对于这些例子,大部分都是包含冗余的属性的,换句话说,实际开发中,适度的保持简洁,是一种推荐的做法。”强迫症“还是不要的好!

6.自动映射

细心的读者可能已经发现,我们上文中出现过集合类型的返回值,但是并没有定义<resultMap>。这其实就是Mybatis在简单的场景之下,直接帮助将结果结合自动转化为List结合的形式。现在,我们稍微深入的了解一下这个过程是如何运行的。

1.当自动映射查询结果时,MyBatis会获取sql返回的列名并在Java类中查找相同名字的属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性,Mybatis会将ID的值赋给id

2.通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,需要将mapUnderscoreToCamelCase设置为true。

自动映射的功能也能够在特殊的resultMap下继续工作。在这种情况下,对于每一个结果映射的集合,所有出现在结果集当中的列,如果没有被手动的设置映射,那么它都会被自动的映射。 在接下来的例子中, id 和 userName列将被自动映射, hashed_password 列将根据配置映射。

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <select id="selectUsers" resultMap="userResultMap">  
  2.   select  
  3.     user_id             as "id",  
  4.     user_name           as "userName",  
  5.     hashed_password  
  6.   from some_table  
  7.   where id = #{id}  
  8. </select>  

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="userResultMap" type="User">  
  2.   <result property="password" column="hashed_password"/>  
  3. </resultMap>  

这里存在3个自动映射的级别:

  • NONE -禁用自动映射,除非手动的映射对应的属性
  • PARTIAL - 会自动的映射结果,除了那些定义在内部的已经存在嵌套的映射
  • FULL - 完全的自动映射
在默认情况下,Mybatis设置为PARTIAL。这么做是有原因的:

当使用FULL级别来实现自动映射时,我们处理联系在一起的结果,和在同一行记录中查询几个不同的实体。由此,这里可能存在一些不期望中的结果。(这句话翻得不好)看看下面这个例子吧:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <select id="selectBlog" resultMap="blogResult">  
  2.   select  
  3.     B.id,  
  4.     B.title,  
  5.     A.username,  
  6.   from Blog B left outer join Author A on B.author_id = A.id  
  7.   where B.id = #{id}  
  8. </select>  

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="blogResult" type="Blog">  
  2.   <association property="author" resultMap="authorResult"/>  
  3. </resultMap>  
  4.   
  5. <resultMap id="authorResult" type="Author">  
  6.   <result property="username" column="author_username"/>  
  7. </resultMap>  

在”Blog“和”Author“的结果集同时使用自动映射这种情况下。注意到:”Author“拥有一个id属性,结果集合中也拥有一个id属性。因此,”Author's“的id将会填充”Blog's“的id。但这并不是我们所期望的,由此就会导致错误。

无论自动映射的界别设置为什么,我们都可以通过”autoMapping“属性来控制开启或者关闭自动映射的功能,具体做法如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <resultMap id="userResultMap" type="User" autoMapping="false">  
  2.   <result property="password" column="hashed_password"/>  
  3. </resultMap>  

-------------------------------------------------------------------------------------------------------------------------------------

至此,Mybatis最入门---ResultMaps高级用法(下)结束


参考资料:

官方文档:http://www.mybatis.org/mybatis-3

0 0
原创粉丝点击