一、数据准备
t_student
t_clazz
创建 pojo:Student、Clazz
创建 mapper 接口:StudentMapper、ClazzMapper
创建 mapper 映射文件:StudentMapper.xml、ClazzMapper.xml
package org.qiu.mybatis.pojo;
import lombok.Data;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.pojo
* @date 2022-10-01-07:06
* @since 1.0
*/
@Data
public class Student {
private Integer sid;
private String sname;
}
package org.qiu.mybatis.pojo;
import lombok.Data;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.pojo
* @date 2022-10-01-07:06
* @since 1.0
*/
@Data
public class Clazz {
private Integer cid;
private String cname;
}
二、多对一
多种方式,常见的包括三种:
-
第一种方式:一条 SQL 语句,级联属性映射。
-
第二种方式:一条 SQL 语句,association。
-
第三种方式:两条 SQL 语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)
第一种方式:级联属性映射
pojo 类 Student 中添加一个属性:Clazz clazz; 表示学生关联的班级对象。
package org.qiu.mybatis.pojo;
import lombok.Data;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.pojo
* @date 2022-10-01-07:06
* @since 1.0
*/
@Data
public class Student {
private Integer sid;
private String sname;
private Clazz clazz;
}
<?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="org.qiu.mybatis.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
</resultMap>
<select id="selectBySid" resultMap="studentResultMap">
select s.*, c.* from t_student s join t_clazz c on s.cid = c.cid where sid = #{sid}
</select>
</mapper>
package com.qiu.mybatis.test;
import org.qiu.mybatis.mapper.StudentMapper;
import org.qiu.mybatis.pojo.Student;
import org.qiu.mybatis.utils.SqlSessionUtil;
import org.junit.Test;
public class StudentMapperTest {
@Test
public void testSelectBySid(){
StudentMapper mapper = SqlSessionUtil.openSession()
.getMapper(StudentMapper.class);
Student student = mapper.selectBySid(1);
System.out.println(student);
}
}
第二种方式:association
其他位置都不需要修改,只需要修改 resultMap 中的配置:association 即可
association 翻译为:关联
学生对象关联一个班级对象
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
第三种方式:分步查询
其他位置不需要修改,只需要修改以及添加以下三处:
第一处:association 中 select 位置填写 sqlId
sqlId=namespace+id
其中 column 属性作为这条子 sql 语句的条件
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="org.qiu.mybatis.mapper.ClazzMapper.selectByCid"
column="cid"/>
</resultMap>
<select id="selectBySid" resultMap="studentResultMap">
select s.* from t_student s where sid = #{sid}
</select>
第二处:在 ClazzMapper 接口中添加方法
package org.qiu.mybatis.mapper;
import org.qiu.mybatis.pojo.Clazz;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.mapper
* @date 2022-10-01-07:17
* @since 1.0
*/
public interface ClazzMapper {
/**
* 根据cid获取Clazz信息
* @param cid
* @return
*/
Clazz selectByCid(Integer cid);
}
第三处:在 ClazzMapper.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="org.qiu.mybatis.mapper.ClazzMapper">
<select id="selectByCid" resultType="Clazz">
select * from t_clazz where cid = #{cid}
</select>
</mapper>
分步优点:
-
代码复用性增强
-
支持延迟加载【暂时访问不到的数据可以先不查询,提高程序的执行效率】
三、多对一延迟加载
要想支持延迟加载,非常简单,只需要在 association 标签中添加 fetchType="lazy"
即可
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid"
column="cid"
fetchType="lazy"/>
</resultMap>
现在只查询学生名字,修改测试程序:
public class StudentMapperTest {
@Test
public void testSelectBySid(){
StudentMapper mapper = SqlSessionUtil.openSession()
.getMapper(StudentMapper.class);
Student student = mapper.selectBySid(1);
// System.out.println(student);
// 只获取学生姓名
String sname = student.getSname();
System.out.println("学生姓名:" + sname);
}
}
如果后续需要使用到学生所在班级的名称,这个时候才会执行关联的 sql 语句,修改测试程序:
public class StudentMapperTest {
@Test
public void testSelectBySid(){
StudentMapper mapper = SqlSessionUtil.openSession()
.getMapper(StudentMapper.class);
Student student = mapper.selectBySid(1);
// System.out.println(student);
// 只获取学生姓名
String sname = student.getSname();
System.out.println("学生姓名:" + sname);
// 到这里之后,想获取班级名字了
String cname = student.getClazz().getCname();
System.out.println("学生的班级名称:" + cname);
}
}
通过以上的执行结果可以看到,只有当使用到班级名称之后,才会执行关联的 sql 语句,这就是延迟加载
在 mybatis 中如何开启全局的延迟加载呢?需要 setting 配置,如下:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
开启全局延迟加载之后,所有的 sql 都会支持延迟加载,如果某个 sql 不希望它支持延迟加载怎么办呢?
将 fetchType 设置为 eager
:
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid"
column="cid"
fetchType="eager"/>
</resultMap>
这样的话,针对某个特定的 sql,就关闭了延迟加载机制。
四、一对多
一对多的实现,通常是在一的一方中有 List 集合属性
在 Clazz 类中添加 List<Student> stus;
属性
package org.qiu.mybatis.pojo;
import lombok.Data;
import java.util.List;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.pojo
* @date 2022-10-01-07:06
* @since 1.0
*/
@Data
public class Clazz {
private Integer cid;
private String cname;
private List<Student> stus;
}
一对多的实现通常包括两种实现方式:
-
第一种方式:collection
-
第二种方式:分步查询
第一种方式:collection
package org.qiu.mybatis.mapper;
import org.qiu.mybatis.pojo.Clazz;
/**
* @author 秋玄
* @version 1.0
* @package org.qiu.mybatis.mapper
* @date 2022-10-01-07:17
* @since 1.0
*/
public interface ClazzMapper {
/**
* 根据cid获取Clazz信息
* @param cid
* @return
*/
Clazz selectByCid(Integer cid);
/**
* 根据班级编号查询班级信息。同时班级中所有的学生信息也要查询。
* @param cid
* @return
*/
Clazz selectClazzAndStusByCid(Integer cid);
}
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="stus" ofType="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
</collection>
</resultMap>
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
select * from t_clazz c join t_student s on c.cid = s.cid where c.cid = #{cid}
</select>
注意是 ofType,表示 “集合中的类型”
package com.powernode.mybatis.test;
import org.qiu.mybatis.mapper.ClazzMapper;
import org.qiu.mybatis.pojo.Clazz;
import org.qiu.mybatis.utils.SqlSessionUtil;
import org.junit.Test;
public class ClazzMapperTest {
@Test
public void testSelectClazzAndStusByCid() {
ClazzMapper mapper = SqlSessionUtil.openSession()
.getMapper(ClazzMapper.class);
Clazz clazz = mapper.selectClazzAndStusByCid(1001);
System.out.println(clazz);
}
}
第二种方式:分步查询
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!--主要看这里-->
<collection property="stus"
select="org.qiu.mybatis.mapper.StudentMapper.selectByCid"
column="cid"/>
</resultMap>
<!--sql语句也变化了-->
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
select * from t_clazz c where c.cid = #{cid}
</select>
五、一对多延迟加载
一对多延迟加载机制和多对一是一样的,同样是通过两种方式:
第一种:fetchType="lazy"
第二种:修改全局的配置 setting,lazyLoadingEnabled=true
,如果开启全局延迟加载,想让某个 sql 不使用延迟加载:fetchType="eager"
一 叶 知 秋,奥 妙 玄 心