MyBatis的关联映射

Posted shi_zi_183

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis的关联映射相关的知识,希望对你有一定的参考价值。

MyBatis的关联映射

实际开发中,对数据库的操作常常会设计多张表,这在面向对象中就设计了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好地处理对象与对象之间的关联关系。

关联关系概述

在关系型数据库中,多表之间存在三种关系,分别是一对一、一对多、多对多
一对一:在任意一方引入对方主键作为外键。
一对多:在"多"的一方,添加"一"的一方的主键作为外键。
多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。
通过数据库中的表可以描述数据之间的关系,同样,在Java中,通过对象也可以进行关系描述
一对一

class A{
	B b;
}
class B{
	A a;
}

一对多

class A{
	List<B> b;
}
class B{
	A a;
}

多对多

class A{
	List<B> b;
}
class B{
	List<A> a;
}

一对一

在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。
MyBatis使用<resultMap>元素中的<association>子元素来处理一对一关联关系。
<association>元素中,通过可以配置以下属性。
1、property:指定映射到的实体类对象属性,与表字段一一对应。
2、column:指定表中对应的字段。
3、javaType:指定映射到实体对象属性的类型。
4、select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。
5、fetchType:指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。
<association>元素的使用非常简单

<association property="card" column="card_id"
		javaType="com.ex.po.IdCard"
		select="com.ex.mapper.IdCardMapper.findCodeById"/>
<association property="card" column="card_id">
	<id property="id" colum="card_id"/>
	<result property="code" column="code"/>
</association>

查询个人及其关联的身份证信息是先通过查询个人表中的主键来获个人信息,然后通过表中的外键,来获取证件表中的身份证号信息。
1)创建数据表。在mybatis数据库中分别创建名为tb_idcard和tb_person的数据表,同时预先插入两个数据

use mybatis;
create table tb_idcard(
	id int primary key auto_increment,
	code varchar(18)
);
insert into tb_idcard(code)values('152221198711020624');
insert into tb_idcard(code)values('152201199008150317');
create table tb_person(
	id int primary key auto_increment,
	name varchar(32),
	age int,
	sex varchar(8),
	card_id int unique,
	foreign key(card_id) references tb_idcard(id)
);
insert into tb_person(name,age,sex,card_id) values('Rose',29,'女',1);
insert into tb_person(name,age,sex,card_id) values('tom',27,'男',2);

2)在Eclipse中创建一个名为chapter09的Web项目,然后引入相关JAR包、log4j日志文件、MybatisUtils工具类以及mybatis-config.xml核心配置文件。
3)在项目的com.ex.po包中创建持久化类IdCard和Person
IdCard.java

package com.ex.po;

public class IdCard {
	private Integer id;
	private String code;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	@Override
	public String toString(){
		return "IdCard [id="+id+", code="+code+"]";
	}
}

4)com.ex.mapper包中,创建证件映射文件IdCardMapper.xml和个人映射文件PersonMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息
IdCardMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ex.mapper.UserMapper">
	<select id="findCodeById" parameterType="Integer" resultType="IdCard">
		select * from tb_idcard where id=#{id}
	</select>
</mapper>

PersonMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ex.mapper.PersonMapper">
	<select id="findCodeById" parameterType="Integer" resultType="IdCardWithPersonResult">
		select * from tb_idcard where id=#{id}
	</select>
	<resultMap type="Person" id="IdCardWithPersonResult">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="age" column="age"/>
		<result property="sex" column="sex"/>
		<association property="card" column="card_id" javaType="IdCard"
			select="com.ex.mapper.IdCardMapper.findCodeById"/>
	</resultMap>
</mapper>

在上述两个映射文件中,使用了MyBatis中的嵌套查询方式进行了个人及其关联的证件信息查询,因为返回的个人对象中除了基本属性外还有一个关联的card属性,所以需要手动编写结果映射。从映射文件PersonMapper.xml中可以看出,嵌套查询的方法是限制性一个简单的SQL语句,然后在进行结束映射时,将关联对象在<association>元素中使用select属性执行另一条SQL语句。
5)在核心配置文件mybatis-config.xml中,引入Mapper映射文件并定义别名。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties"/>
	<typeAliases>
		<package name="com.ex.po"/>
	</typeAliases>
	 <environments default="mysql">
		 <environment id="mysql">
		 <transactionManager type="JDBC"/>
			 <dataSource type="POOLED">
				 <property name="driver" value="${jdbc.driver}"/>
				 <property name="url" value="${jdbc.url}"/>
				 <property name="username" value="${jdbc.username}"/>
				 <property name="password" value="${jdbc.password}"/>
			 </dataSource>
		 </environment>
	 </environments>
	 <mappers>
		 <mapper resource="com/ex/mapper/IdCardMapper.xml"/>
		 <mapper resource="com/ex/mapper/PersonMapper.xml"/>
	 </mappers>
</configuration>

6)在test包中,创建测试类MybatisAssociatedTest,并在类中编写测试方法

package com.ex.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.ex.po.*;
import com.ex.utils.*;
public class MybatisAssociatedTest {
	@Test
	public void findPersonByIdTest(){
		SqlSession session=MybatisUtils.getSession();
		Person person=session.selectOne("com.ex.mapper."
				+ "PersonMapper.findPersonById", 1);
		System.out.println(person);
		session.close();
	}
}


虽然使用嵌套查询的方式比较简单,MyBatis嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这些可能会导致成百上千条关联的SQL语句被执行,从而极大的消耗数据库性能并会降低查询效率。为此,我们可以使用MyBatis提供的嵌套结果方式,来进行关联查询。
在PersonMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ex.mapper.PersonMapper">
	<select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
		select * from tb_person where id=#{id}
	</select>
	<resultMap type="Person" id="IdCardWithPersonResult">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="age" column="age"/>
		<result property="sex" column="sex"/>
		<association property="card" javaType="IdCard">
			<id property="id" column="card_id"/>
			<result property="code" column="code"/>
		</association>
	</resultMap>
</mapper>


可以看到MyBatis只执行了一条复杂的SQL。
MyBatis延迟加载的配置
在使用MyBatis嵌套查询方式进行Mybatis关联查询映射时,使用延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件mybatis-config.xml中的<settings>元素内进行配置

	 <settings>
	 	<setting name="lazyLoadingEnabled" value="true"/>
	 	<setting name="aggressiveLazyLoading" value="false"/>
	 </settings>

MyBatis关联映射的<association>元素和<collection>元素中都已默认配置了延迟加载属性,即默认属性fetchType=“lazy”(fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。

package com.ex.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.ex.po.*;
import com.ex.utils.*;
public class MybatisAssociatedTest {
	@Test
	public void findPersonByIdTest(){
		SqlSession session=MybatisUtils.getSession();
		Person person=session.selectOne("com.ex.mapper."
				+ "PersonMapper.findPersonById", 1);
		System.out.println("访问嵌套外的属性name"+person.getName());
		System.out.println("访问嵌套内的属性code"+person.getCard().getCode());
		System.out.println(person);
		session.close();
	}
}


可以看到,只有当访问嵌套内内容的时候才会发送嵌套sql,这样可以有效的避免资源浪费,只请求需要的。

以上是关于MyBatis的关联映射的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis关联查询,表字段相同,resultMap映射问题的解决办法

mybatis第二天——大纲待更新

mybatis第二天

MyBatis关联映射

Mybatis关联映射

EF添加关联的提示问题:映射从第 260 行开始的片段时有问题: