MyBatis 入门到精通(三) 高级结果映射

MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的。虽然我们希望所有的数据库遵守第三范式或BCNF(修正的第三范式),但它们不是。如果有一个数据库能够完美映射到所有应用程序,也将是非常棒的,但也没有。结果集映射就是MyBatis为解决这些问题而提供的解决方案。例如,我们如何映射下面这条语句?

[html] view plaincopy

  1. <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result">
  2. select
  3. b.id,
  4. b.title,
  5. b.author_id,
  6. a.id,
  7. a.username,
  8. a.password,
  9. a.email,
  10. a.bio
  11. from
  12. Blog b left join Author a
  13. on
  14. b.author_id = a.id
  15. where
  16. b.id = #{id}
  17. </select>

[html] view plaincopy

  1. <resultMap type="Blog" id="Blog_result">
  2. <id column="id" property="id" />
  3. <result column="title" property="title"/>
  4. <!-- 映射关联的对象 -->
  5. <association property="author" javaType="Author">
  6. <id column="author_id" property="id"/>
  7. <result column="username" property="username"/>
  8. <result column="password" property="password"/>
  9. <result column="email" property="email"/>
  10. <result column="bio" property="bio"/>
  11. </association>
  12. </resultMap>

resultMap

resultMap属性:type为java实体类;id为此resultMap的标识。

resultMap可以设置的映射:

1. constructor – 用来将结果反射给一个实例化好的类的构造器

a) idArg – ID 参数;将结果集标记为ID,以方便全局调用
b) arg –反射到构造器的通常结果

2. id – ID 结果,将结果集标记为ID,以方便全局调用

3. result – 反射到JavaBean 属性的普通结果

4. association – 复杂类型的结合;多个结果合成的类型

a) nested result mappings – 几resultMap 自身嵌套关联,也可以引用到一个其它上

5. collection –复杂类型集合a collection of complex types

6. nested result mappings – resultMap 的集合,也可以引用到一个其它上

7. discriminator – 使用一个结果值以决定使用哪个resultMap

a) case – 基本一些值的结果映射的case 情形

i. nested result mappings –一个case 情形本身就是一个结果映射,因此也可以包括一些相同的元素,也可以引用一个外部resultMap。

è最佳实践:逐步地生成resultMap,单元测试对此非常有帮助。如果您尝试一下子就生成像上面这样巨大的resultMap,可能会出错,并且工作起来非常吃力。从简单地开始,再一步步地扩展,并且进行单元测试。使用框架开发有一个缺点,它们有时像是一个黑合。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交bugs也非常有用。

下面一部分将详细说明每个元素。

id、result

id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。

[html] view plaincopy

  1. <resultMap type="Blog" id="Blog_result">
  2. <id column="id" property="id" />
  3. <result column="title" property="title"/>
  4. </resultMap>

这些是结果映射最基本内容。id和result都映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。

这两者之间的唯一不同是id表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)。

id、result语句属性配置细节:


属性


描述


property


映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。

否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。

比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。


column


从数据库中得到的列名,或者是列名的重命名标签。

这也是通常和会传递给resultSet.getString(columnName)方法参数中相同的字符串。


javaType


一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。

如果你映射到一个JavaBean,MyBatis通常可以断定类型。

然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。


jdbcType


在这个表格之后的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。

这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。


typeHandler


我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。

这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。

支持的JDBC类型

MyBatis支持如下的JDBC类型:


BIT


FLOAT


CHAR


TIMESTAMP


OTHER


UNDEFINED


TINYINT


REAL


VARCHAR


BINARY


BLOB


NVARCHAR


SMALLINT


DOUBLE


LONGVARCHAR


VARBINARY


CLOB


NCHAR


INTEGER


NUMERIC


DATE


LONGVARBINARY


BOOLEAN


NCLOB


BIGINT


DECIMAL


TIME


NULL


CURSOR

 

constructor <构造方法>

[html] view plaincopy

  1. <resultMap type="Blog" id="Blog_result_cs">
  2. <constructor>
  3. <idArg column="id" javaType="int"/>
  4. <arg column="title" javaType="String"/>
  5. </constructor>
  6. </resultMap>

对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像绝大多数的领域模型,指令也许是你想使用一成不变的类的地方。通常包含引用或查询数据的表很少或基本不变的话对一成不变的类来说是合适的。构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis也支持私有属性和私有JavaBeans属性来达到这个目的,但是一些人更青睐构造方法注入。Constructor(构造方法)元素支持这个。

[java] view plaincopy

  1. package com.accp.mybatis.model;
  2. public class Blog {
  3. private Integer id;
  4. private String title;
  5. private Integer authorId;
  6. public Blog() {
  7. }
  8. public Blog(Integer id, String title, Integer authorId) {
  9. super();
  10. System.out.println("使用有参Blog");
  11. this.id = id;
  12. this.title = title;
  13. this.authorId = authorId;
  14. }
  15. //........
  16. }

为了向这个构造方法中注入结果,MyBatis需要通过它的参数的类型来标识构造方法。Java没有自查(或反射)参数名的方法。所以当创建一个构造方法元素时,保证参数是按顺序排列的,而且数据类型也是确定的。

association<关联映射>

[html] view plaincopy

  1. <!-- 映射关联的对象 -->
  2. <association property="author" javaType="Author">
  3. <id column="author_id" property="id"/>
  4. <result column="username" property="username"/>
  5. <result column="password" property="password"/>
  6. <result column="email" property="email"/>
  7. <result column="bio" property="bio"/>
  8. </association>

关联元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。关联映射就工作于这种结果之上。你指定了目标属性,来获取值的列,属性的java类型(很多情况下MyBatis可以自己算出来),如果需要的话还有jdbc类型,如果你想覆盖或获取的结果值还需要类型控制器。
关联中不同的是你需要告诉MyBatis如何加载关联。MyBatis在这方面会有两种不同的方式:
(1) 嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型。
(2) 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。
首先,然让我们来查看这个元素的属性。


属性


描述


property


映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。


column


来自数据库的列名,或重命名的列标签。这和通常传递给resultSet.getString(columnName)方法的字符串是相同的。

注意:要处理复合主键,你可以指定多个列名通过column=”{prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引起prop1和prop2以参数对象形式来设置给目标嵌套查询语句。


javaType


一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。如果你映射到一个JavaBean,MyBatis通常可以断定类型。然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。


jdbcType


在这个表格之前的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。


typeHandler


我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。

联合嵌套选择(Nested Select for Association)


select


通过这个属性,通过ID引用另一个加载复杂类型的映射语句。从指定列属性中返回的值,将作为参数设置给目标select 语句。表格下方将有一个例子。注意:在处理组合键时,您可以使用column=”{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1和prop2设置到目标嵌套语句的参数对象中。

SELECT<联合查询>

[html] view plaincopy

  1. <resultMap type="Blog" id="Blog_result">
  2. <association property="author" column="author_id"
  3. javaType="Author" select="selectAuthorById" />
  4. </resultMap>
  5. <select id="selectAuthorById" parameterType="int" resultType="Author">
  6. select * from Author where id = #{id}
  7. </select>
  8. <!--
  9. select关联,建议在一对一的情况下使用。
  10. 在此处,如果selectBlogById返回多个Blog,将会带来N+1问题
  11. -->
  12. <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
  13. select * from Blog where id = #{id}
  14. </select>

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描述了“selectAuthorById”语句应该被用来加载它的author属性。
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单,但是对于大型数据集合和列表将不会表现很好。问题就是我们熟知的“N+1查询问题”。概括地讲,N+1查询问题可以是这样引起的:
    (1)你执行了一个单独的SQL语句来获取结果列表(就是“+1”)。
    (2)对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

关联的嵌套结果


resultMap


这是结果映射的ID,可以映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis让你“链接”结果映射,来处理嵌套结果。例子会很容易来仿照,这个表格后面也有一个示例。

[html] view plaincopy

  1. <select id="selectBlog" parameterType="int" 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. A.id as author_id,
  7. A.username as author_username,
  8. A.password as author_password,
  9. A.email as author_email,
  10. A.bio as author_bio
  11. From Blog B left outer join Author A on B.author_id = A.id
  12. where B.id = #{id}
  13. </select>

注意这个联合查询,以及采取保护来确保所有结果被唯一而且清晰的名字来重命名。这使得映射非常简单。现在我们可以映射这个结果:

[html] view plaincopy

  1. <resultMap id="blogResult" type="Blog">
  2. <id property=”blog_id” column="id" />
  3. <result property="title" column="blog_title"/>
  4. <association property="author" column="blog_author_id"
  5. javaType="Author" resultMap=”authorResult”/>
  6. </resultMap>
  7. <resultMap id="authorResult" type="Author">
  8. <id property="id" column="author_id"/>
  9. <result property="username" column="author_username"/>
  10. <result property="password" column="author_password"/>
  11. <result property="email" column="author_email"/>
  12. <result property="bio" column="author_bio"/>
  13. </resultMap>

非常重要:在嵌套结果映射中id元素扮演了非常重要的角色。应该通常指定一个或多个属性,它们可以用来唯一标识结果。实际上就是如果你不使用它(id元素),但是会产生一个严重的性能问题,不过MyBatis仍然可以正常工作。选择的属性越少越好,它们可以唯一地标识结果。主键就是一个显而易见的选择(即便是联合主键)。
现在,上面的示例用了外部的结果映射元素来映射关联。这使得Author结果映射可以重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描述的结果映射中。你可以嵌套结果映射。这里给出使用这种方式的相同示例:

[html] view plaincopy

  1. <resultMap id="blogResult" type="Blog">
  2. <id property=”blog_id” column="id" />
  3. <result property="title" column="blog_title"/>
  4. <association property="author" column="blog_author_id"
  5. javaType="Author">
  6. <id property="id" column="author_id"/>
  7. <result property="username" column="author_username"/>
  8. <result property="password" column="author_password"/>
  9. <result property="email" column="author_email"/>
  10. <result property="bio" column="author_bio"/>
  11. </association>
  12. </resultMap>

collection<集合>

[html] view plaincopy

  1. <collection property="posts" ofType="Post">
  2. <id property="id" column="id"/>
  3. <result property="subject" column="subject"/>
  4. <result property="body" column="body"/>
  5. </collection>

集合元素的作用几乎和关联是相同的。实际上,它们也很相似。
我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章。在博客类中,这可以由下面这样的写法来表示:
private List<Post> posts;
要映射嵌套结果集合到List中,我们使用集合元素。就像关联元素一样,我们可以从连接中使用嵌套查询,或者嵌套结果。

集合的嵌套查询

首先,让我们看看使用嵌套查询来为博客加载文章。

[html] view plaincopy

  1. <resultMap type="Blog" id="Blog_result">
  2. <association property="author" column="author_id"
  3. javaType="Author" select="selectAuthorById" />
  4. <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />
  5. </resultMap>
  6. <select id="selectBlogPost" resultType="Post" parameterType="int">
  7. select * from Post where blog_id=#{id}
  8. </select>
  9. <select id="selectAuthorById" parameterType="int" resultType="Author">
  10. select * from Author where id = #{id}
  11. </select>
  12. <!--
  13. select关联,建议在一对一的情况下使用。
  14. 在此处,如果selectBlogById返回多个Blog,将会带来N+1问题
  15. -->
  16. <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
  17. select * from Blog where id = #{id}
  18. </select>

首先,你应该注意我们使用的是集合元素。然后要注意那个新的“ofType”属性。这个属性用来区分JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个映射:

[html] view plaincopy

  1. <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />

读作:“在Post类型的ArrayList中的posts的集合。”

集合的嵌套结果

[html] view plaincopy

  1. <select id="selectBlog" parameterType="int" 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>

现在用文章映射集合映射博客,可以简单写为:

[html] view plaincopy

  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元素的重要性。
如果你引用更长的形式允许你的结果映射的更多重用,你可以使用下面这个替代的映

[html] view plaincopy

  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”/>
  5. </resultMap>
  6. <resultMap id="blogPostResult" type="Post">
  7. <id property="id" column="post_id"/>
  8. <result property="subject" column="post_subject"/>
  9. <result property="body" column="post_body"/>
  10. </resultMap>

discriminator鉴别器

[html] view plaincopy

  1. <discriminator javaType="int" column="draft">
  2. <case value="1" resultType="DraftPost"/>
  3. </discriminator>

有时一个单独的数据库查询也许返回很多不同(但是希望有些关联)数据类型的结果集。鉴别器元素就是被设计来处理这个情况的,还有包括类的继承层次结构。鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句。
定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。JavaType是需要被用来保证等价测试的合适类型(尽管字符串在很多情形下都会有用)。比如:

[html] view plaincopy

  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>
  15. <resultMap id="carResult" type="Car">
  16. <result property=”doorCount” column="door_count" />
  17. </resultMap>

上面示例中,MyBatis会从结果集中得到每条记录,然后比较它的vehicle类型的值。如果它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。
还有另外一种语法来做简洁的映射风格。比如:

[html] view plaincopy

    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>
时间: 2024-11-10 14:12:56

MyBatis 入门到精通(三) 高级结果映射的相关文章

MyBatis 入门到精通(二) SQL语句映射XML文件

MyBatis 真正强大之处就在这些映射语句,也就是它的魔力所在.对于它的强大功能,SQL 映射文件的配置却非常简单. 如果您比较SQL 映射文件配置与JDBC 代码,您很快可以发现,使用SQL 映射文件配置可以节省95%的代码量.MyBatis 被创建来专注于SQL,但又给您自己的实现极大的空间. 需要配置的基本元素 1. cache – 配置给定模式的缓存 2. cache-ref – 从别的模式中引用一个缓存 3. resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集

mybatis从入门到精通(三) 结果映射

mybatis从入门到精通(三) 结果映射 一丶什么是结果映射 结果映射是用于将数据库表字段和实体类中的属性名映射起来, 即究竟是哪个字段名与属性名对应. 映射之后, 即可通过mybatis将从数据库查询的结果转换成对应的实体类对象类型, 除去了人工转换的麻烦. 二丶自动映射 所谓的自动映射, 即是默认java实体类的属性名是驼峰式, 而数据库表字段名是以下划线分隔, 如 属性名userName 与 表字段user_name相对应, 如果是这种简单通用的规则, 即可通过配置mybatis, 使得

visual studio 2015 搭建python开发环境,python入门到精通[三]

在上一篇博客Windows搭建python开发环境,python入门到精通[一]很多园友提到希望使用visual studio 2013/visual studio 2015 python做demo,这里略带一句,其实就"学习python"而言,比较建议使用pycharm,pycharm可以设置VS或者eclipse等多种IDE的编码,可以让绝大部分其他语言使用IDE的习惯者更容易上手.这一点兼容确实做的很好.不过既然这么多园友要求使用vs开发python的话,就介绍一下visual

mybatis实战教程(mybatis in action),mybatis入门到精通

转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过hibernate了那这个就非常的简单) (再加一条,其实大家可以看官方的教程更好些:http://mybatis.github.io/mybatis-3/,而且如果英文不是很好的那就看中文的:http://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html) 写在这个系列前面的话: 以前曾经用过ibat

mybatis实战教程(mybatis in action),mybatis入门到精通(转)

转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过Hibernate了那这个就非常的简单) (再加一条,其实大家可以看官方的教程更好些:http://mybatis.github.io/mybatis-3/,而且如果英文不是很好的那就看中文的:http://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html) 写在这个系列前面的话: 以前曾经用过ibat

mybatis实战教程,mybatis入门到精通

转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过Hibernate了那这个就非常的简单) (再加一条,其实大家可以看官方的教程更好些:http://mybatis.github.io/mybatis-3/,而且如果英文不是很好的那就看中文的:http://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html) 写在这个系列前面的话: 以前曾经用过ibat

MyBatis入门学习(三)

在实际开发中,我们希望文件配置是分类配置存放,需要的时候引入即可.如果多文件的配置混合配置使用,那么对项目的后期维护将增加难度. 一.对于连接数据库的配置单独放在一个properties文件中 1.对于之前的配置我们是将数据库配置放置在conf.xml文件中,如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//

Spring入门到精通&lt;三&gt;Spring访问数据库

Spring在数据访问方面提供了模板化和Dao支持类的提供,方便操作数据库. 一.Spring JDBC 1.配置数据源 DataSource 不管选择哪一种Spring Dao的支持方式,你都需要配置一个数据源的引用.配置方式有三种: ①.使用JNDI查找数据源: ②.使用数据源连接池:(dbcp c3p0 druid)  <!-- 配置数据源 -->      <bean id="dataSource" class="org.apache.commons

Mybatis学习第22节 -- 高级结果映射 构造方法映射

知识储备:  对于大部分编程语言的函数来说, 函数的signature是函数名和函数参数,而对于函数参数的名称, 编译器不关心. 为ShopCustom创建一个构造函数 public ShopCustom(Integer id, String shopName, String shopDesc) { this.id = id; this.shopName = shopName; this.shopDesc = shopDesc;} 为ShopMapperCustom添加一个新方法 public