Mybatis映射详解

本文详细介绍了MyBatis中的复杂映射方法,包括constructor、association、collection和discriminator等元素的应用,并通过实例展示了如何处理多级关联及不同类型的结果映射。
摘要由CSDN通过智能技术生成

Mybatis映射详解

在最近的工作中,碰到一个比较复杂的返回结果,发现简单映射已经解决不了这个问题了,只好去求助百度,学习mybatis复杂映射应该怎么写,将学习笔记结合工作碰到的问题写下本文,供自身查漏补缺,同时已被不时之需。

ResultMap的属性列表

属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名。
autoMapping如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

1. 简单映射

select查询的返回结果可以通过映射将表字段名与类属性名一一对应:

package com.someapp.model;
@Data
public class User {
  private int id;
  private String username;
  private String hashedPassword;
}
<!--type 标签对应Java实体-->
<resultMap id="userResultMap" type="User">
  <id property="id" column="id" />
  <result property="username" column="user_name"/>
  <result property="hashedPassword" column="hashed_password"/>
</resultMap>

<select id="selectUsers" resultMap="userResultMap">
  select id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

2. 复杂映射

  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator – 使用结果值来决定使用哪个 resultMap
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
2.1 id和result

idresult元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。这两者之间的唯一不同是,id元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。

id和result属性

属性描述
property映射到列结果的字段或属性。如果 JavaBean有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。
column数据库中的列名,或者是列的别名
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名)。
jdbcTypeJDBC 类型,只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。
typeHandler使用这个属性,你可以覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
2.2 constructor(构造方法)

构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。constructor 元素就是为此而生的。

属性描述
property映射到列结果的字段或属性。如果 JavaBean有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。
column数据库中的列名,或者是列的别名
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名)。
jdbcTypeJDBC 类型,只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。
typeHandler使用这个属性,你可以覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
select用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。
resultMap结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。
name构造方法形参的名字。
<constructor>
   <idArg column="id" javaType="int" name="id" />
   <arg column="age" javaType="_int" name="age" />
   <arg column="username" javaType="String" name="username" />
</constructor>
2.3 association (关联)

关联(association)元素处理“有一个”类型的关系。关联结果映射和其它类型的映射工作方式差不多。你需要指定目标属性名以及属性的javaType,在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。

association (关联)属性

属性描述
property映射到列结果的字段或属性。如果 JavaBean有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名)。
jdbcTypeJDBC 类型,只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。
typeHandler使用这个属性,你可以覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
<association property="author" column="blog_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
</association>

关联的嵌套 Select 查询

我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。(不推荐使用)

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

关联的嵌套结果映射

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
  </association>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  select
    B.id            as blog_id,
    B.title         as blog_title,
    B.author_id     as blog_author_id,
    A.id            as author_id,
    A.username      as author_username,
    A.password      as author_password,
    A.email         as author_email,
    A.bio           as author_bio
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>

id 元素在嵌套结果映射中扮演着非常重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性。虽然,即使不指定这个属性,MyBatis 仍然可以工作,但是会产生严重的性能问题。只需要指定可以唯一标识结果的最少属性。显然,你可以选择主键(复合主键也可以)。

2.4 collection(集合)
<collection property="posts" ofType="domain.blog.Post">
  <id property="id" column="post_id"/>
  <result property="subject" column="post_subject"/>
  <result property="body" column="post_body"/>
</collection>

集合的嵌套 Select 查询

<resultMap id="blogResult" type="Blog">
    <!-- “posts 是一个存储 Post 的 ArrayList 集合” -->
  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectPostsForBlog" resultType="Post">
  SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>

“ofType” 属性:用来将JavaBean(或字段)属性的类型和集合存储的类型区分开来。

集合的嵌套结果映射

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <result property="body" column="post_body"/>
  </collection>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  select
  B.id as blog_id,
  B.title as blog_title,
  B.author_id as blog_author_id,
  P.id as post_id,
  P.subject as post_subject,
  P.body as post_body,
  from Blog B
  left outer join Post P on B.id = P.blog_id
  where B.id = #{id}
</select>
2.5 discriminator(鉴别器)

有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。

一个鉴别器的定义需要指定 column 和 javaType 属性。column 指定了 MyBatis 查询被比较值的地方。而 javaType 用来确保使用正确的相等测试。例如:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

<resultMap id="carResult" type="Car" extends="vehicleResult">
  <result property="doorCount" column="door_count" />
</resultMap>

在这个示例中,MyBatis 会从结果集中得到每条记录,然后比较它的 vehicle type 值。如果它匹配任意一个鉴别器的 case,就会使用这个 case 指定的结果映射。这个过程是互斥的,也就是说,剩余的结果映射将被忽略。如果不能匹配任何一个 case,MyBatis 就只会使用鉴别器块外定义的结果映射。

2.5 应用实例
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>

3 自动映射

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

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

甚至在提供了结果映射后,自动映射也能工作。在这种情况下,对于每一个结果映射,在ResultSet 出现的列,如果没有设置手动映射,将被自动映射。在自动映射处理完毕后,再处理手动映射。在下面的例子中,iduserName 列将被自动映射,hashed_password 列将根据配置进行映射。

<resultMap id="userResultMap" type="User">
  <result property="password" column="hashed_password"/>
</resultMap>

<select id="selectUsers" resultMap="userResultMap">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password
  from some_table
  where id = #{id}
</select>

有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射(默认映射等级)。
  • FULL - 自动映射所有属性。

无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping属性来为指定的结果映射设置启用/禁用自动映射。

<resultMap id="userResultMap" type="User" autoMapping="false">
  <result property="password" column="hashed_password"/>
</resultMap>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值