当前位置 博文首页 > 龚厂长的博客:mybatis解析-association实现原理详解

    龚厂长的博客:mybatis解析-association实现原理详解

    作者:[db:作者] 时间:2021-07-26 14:45

    本文基于mybatis-spring 1.3.1和mybatis 3.4.4版本

    mybatis中的标签association主要用于解决“有一个”类型的关系,它表示一个对象至多有一个关联对象。比如一般情况下,每个人都一个身份证,可能有些人没有(比如黑户和婴儿),人和身份证的关系就可以使用association表示。
    本文接下来介绍association的三种用法,然后介绍其实现原理。

    一、使用association

    先定义两个类,一个是Person,一个是IDCard,分别表示人和身份证。

    public class Person{
        private int id;
        private String name;
        private String sex;
        private Date birthday;
        private IdCard card;//IdCard类为Person的属性
        //get和set方法省略
    }
    public class IdCard{
        private int id;
        private String number;
        private Date expiredTime;
    	//get和set方法省略
    }
    

    这两个类分别对应了两个表:

    CREATE TABLE `person` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(10)  NOT NULL,
      `sex` varchar(2)  DEFAULT NULL,
      `birthday` date DEFAULT NULL,
      `card_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    );
    CREATE TABLE `id_card` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `number` varchar(255)  DEFAULT NULL,
      `expiredTime` date DEFAULT NULL,
      PRIMARY KEY (`id`)
    );
    

    以如下SQL语句做介绍:

    select * from person p,id_card card where p.card_id=card.id and name='张三';
    

    上面这个SQL语句一次性查询出了Person和IdCard两个对象的数据,我想让mybatis返回一个Person对象,并且Person对象里面组装好了IdCard对象,现在来看看如何使用标签association达成这个目标。

    方法一
    在resultMap标签里面嵌套association标签,resultMap表示Person对象,association表示IdCard对象。

      <resultMap id="associationResultMap" type="com.crawl.db.domain.Person">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="sex" jdbcType="VARCHAR" property="sex" />
        <result column="birthday" jdbcType="DATE" property="birthday" />
        <association property="idCard" column="card_id" javaType="com.crawl.db.domain.IdCard">
          <id column="cid" jdbcType="INTEGER" property="id" />
          <result column="number" jdbcType="VARCHAR" property="number" />
          <result column="expiredTime" jdbcType="DATE" property="expiredtime" />
        </association>
      </resultMap>
      
      <select id="findPersonByName" parameterType="java.lang.String" resultMap="associationResultMap">
        select p.*,card.id as cid,card.number as number,card.expiredTime expiredTime from person p,id_card card where p.card_id=card.id and name=#{name,jdbcType=VARCHAR}
      </select>
    

    执行findPersonByName可以得到一个Person对象,且Person对象关联上了对应的IdCard对象。
    注意association子标签里面的column属性不能与resultMap里面的column属性有相同的值,否则造成association的对象的属性值赋值错误。而且association标签里面的column属性不是必须的,对于上面的例子也就是column="card_id" 是可以去掉的。
    方法二
    如果采用方法一,当有另外一个resultMap也想嵌套相同的association时,那么需要在内部再定义一次,这会造成重复,因此可以将相同的association提取出来,写成如下形式:

      <resultMap id="associationResultMap" type="com.crawl.db.domain.Person">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="sex" jdbcType="VARCHAR" property="sex" />
        <result column="birthday" jdbcType="DATE" property="birthday" />
        <association property="idCard"  javaType="com.crawl.db.domain.IdCard" resultMap="cardResultMap"/>
      </resultMap>
    
      <resultMap id="cardResultMap" type="com.crawl.db.domain.IdCard">
        <id column="cid" jdbcType="INTEGER" property="id" />
        <result column="number" jdbcType="VARCHAR" property="number" />
        <result column="expiredTime" jdbcType="DATE" property="expiredtime" />
      </resultMap>
    

    方法三
    方法一和方法二都是一次将所有的属性查询出来,但是可能程序中只需要Person,不需要IdCard,那有没有一种方法可以在需要的时候才查询IdCard?
    答案是有。这也是association的第三个应用:关联的嵌套 Select 查询。方法一和方法二叫做关联的嵌套结果映射。

     <resultMap id="associationResultMap" type="com.crawl.db.domain.Person">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="sex" jdbcType="VARCHAR" property="sex" />
        <result column="birthday" jdbcType="DATE" property="birthday" />
        <association property="idCard" column="card_id" javaType="com.crawl.db.domain.IdCard"
          select="findIdCard" fetchType="lazy"/>
      </resultMap>
      
      <select id="