当前位置 博文首页 > 龚厂长的博客:mybatis解析-association实现原理详解
本文基于mybatis-spring 1.3.1和mybatis 3.4.4版本
mybatis中的标签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="