Mybatis笔记(4)-多表查询和延迟加载
Mybatis多表查询
一对一(<association>)
以学生(Student)和身份证(Card)为例,即一个学生对应一个身份证。
数据表如下:(表中的字段除了3个id字段,其他都是varchar类型,card_id是指向card的id字段的外键)
card:
student:
(省略了setter等)
Card类:
public class Card {
private Integer id;
private String idNumber;
}
Student类:
public class Student {
private Integer id;
private String studentName;
private Card card;
}
编写StudentMapper方法:
List<Student> selectAll();
映射文件(查询全部,并Student对应的Card封装进Student):
-
sql语句使用的是等值连接查询。
-
cardMap和studentMap的id标签中的column属性值,分别是cid和sid,对应查询语句结果集的字段名,这里为s.id和c.id设置了别名。
此处column属性值不能使用s.id和c.id,mybatis将无法解析。所以可通过为字段指定别名,将column值指定为对应别名。
-
studentMap中使用了
<association>标签来封装Student中的Card对象。
<resultMap id="cardMap" type="card">
<id property="id" column="cid" />
<result property="idNumber" column="id_number" />
</resultMap>
<resultMap id="studentMap" type="student">
<id property="id" column="sid" />
<result property="studentName" column="student_name" />
<association property="card" resultMap="cardMap" />
</resultMap>
<select id="selectAll" resultMap="studentMap">
select s.id sid,student_name,c.id cid,id_number
from student s
inner join card c
on s.card_id=c.id;
</select>
测试方法:
@Test
public void testSelectALl(){
try(SqlSession session = factory.openSession()){
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.selectAll();
for(Student student : students){
System.out.println(student);
}
}
}
输出:
Student(id=2, studentName=小明, card=Card(id=1, idNumber=111111))
Student(id=1, studentName=张三, card=Card(id=2, idNumber=222222))
Student(id=3, studentName=王大锤, card=Card(id=3, idNumber=333333))
一对多(<collection>)
以班级(grade)和老师(Teacher)为例,一个班级对应多个老师
grade表:
teacher表:(grade_id为指向grade的外键)
对应的JavaBean:
public class Grade {
private Integer id;
private String gradeName;
private List<Teacher> teachers;
}
public class Teacher {
private Integer id;
private String teacherName;
}
//省略了setter、getter、toString
定义Mapper方法:
List<Grade> selectAll();
映射文件:
- 先使用
<resultMap>分别对两个表跟JavaBean进行映射。 - gradeMap中使用了
<collection>封装Grade中的Teacher,通过resultMap属性指定集合类型。property属性指定集合对应的JavaBean属性。 - 与一对一中一样,注意别名问题。
<resultMap id="teacherMap" type="teacher">
<id property="id" column="tid" />
<result property="teacherName" column="teacher_name" />
</resultMap>
<resultMap id="gradeMap" type="grade">
<id property="id" column="gid" />
<result property="gradeName" column="grade_name" />
<collection property="teachers" resultMap="teacherMap" />
</resultMap>
<select id="selectAll" resultMap="gradeMap">
select g.id gid, g.grade_name, t.id tid,t.teacher_name
from grade g
inner join teacher t
on t.grade_id=g.id;
</select>
测试方法:
@Test
public void testSelectAll(){
try(SqlSession session = factory.openSession()){
GradeMapper mapper = session.getMapper(GradeMapper.class);
List<Grade> grades = mapper.selectAll();
for(Grade grade : grades){
System.out.println(grade);
}
}
}
输出:可看到与实际表数据对应。
Grade(id=2, gradeName=软件工程, teachers=[Teacher(id=1, teacherName=黄老师), Teacher(id=2, teacherName=徐老师), Teacher(id=3, teacherName=张老师)])
Grade(id=1, gradeName=计算机科学与技术, teachers=[Teacher(id=4, teacherName=李老师)])
Grade(id=3, gradeName=信息安全, teachers=[Teacher(id=5, teacherName=王老师), Teacher(id=6, teacherName=马老师)])
多对多
多对多其实本质就是一对多,相关的配置与一对多一样。
小结
在上述示例的映射文件中,都是先使用</resultMap>封装好表与JavaBean之间的映射,再在<association>和<collection>中使用resultMap属性指定即可。这样配置代码更加整洁。
也可以在<association>和<collection>中配置映射关系,如一对一中的studentMap可改为:
<resultMap id="studentMap" type="student">
<id property="id" column="sid" />
<result property="studentName" column="student_name" />
<!--<association property="card" resultMap="cardMap" />-->
<association property="card" javaType="card">
<id property="id" column="cid" />
<result property="idNumber" column="id_number" />
</association>
</resultMap>
其中,替代resultMap属性,通过javaType指定封装的类型,对于<collection>,则是使用ofType属性。
Mybatis延迟加载
概述
在多表查询的一对多中,在查询“一”时,有时并不需要将“多”也全部查询出来,什么时候需要用到,什么时候再查询才更符合实际要求,不用造成不必要的系统开销。
在四种表关系中:一对多、多对多,通常情况下都是采用延迟加载;而一对一(跟多对一),通常情况下“一”都不可省略,所以一般都是采用立即加载(也有使用延迟加载的情况)。
Mybatis中可通过修改配置信息实现延迟加载。
实现延迟加载
实现延迟加载需要先设置mybatis的全局变量lazyLoadingEnabled为true(该值默认为false) ,表示开启延迟加载。
若是3.4.1 及之前的版本,还需设置
aggressiveLazyLoading变量为false,其默认值是true。该变量的官方解释:
开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载
以上配置在主配置文件的settings标签中进行:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<!--<setting name="aggressiveLazyLoading" value="false"/>-->
</settings>
通过示例验证:
以学生(Student)和身份证(Card)为例,即一个学生对应一个身份证。
数据表如下:
card:
student:
Card类:
public class Card {
private Integer id;
private String idNumber;
public Card() {
//被创建时输出内容。用于验证延迟加载
System.out.println("创建了一个Card对象...");
}
}
Student类:
public class Student {
private Integer id;
private String studentName;
private Card card;
}
两个JavaBean对应的Mapper:
public interface CardMapper { Card selectById(Integer id); }
public interface StudentMapper { List<Student> selectAll(); }
映射文件:
CardMapper.xml
<mapper namespace="com.mapper.CardMapper">
<resultMap id="cardMap" type="card">
<result property="idNumber" column="id_number" />
</resultMap>
<select id="selectById" resultMap="cardMap">
select * from card
where id = #{id}
</select>
</mapper>
StudentMapper.xml:
- selectAll中的sql语句不用使用连接查询语句。
- 在association标签中,select指定了查询Student中的Card时使用的查询方法,属性值是Mapper方法的全限定名。column属性指定了用于查询card所需的id,即
select * from student的查询结果集中的card_id字段。
<mapper namespace="com.mapper.StudentMapper">
<resultMap id="studentMap" type="student">
<id property="id" column="id" />
<result property="studentName" column="student_name" />
<association property="card" column="card_id" select="com.mapper.CardMapper.selectById" />
</resultMap>
<select id="selectAll" resultMap="studentMap">
select * from student
</select>
</mapper>
测试方法:
@Test
public void testLazyLoad(){
try(SqlSession session = factory.openSession()){
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.selectAll();
Student student = students.get(0);
System.out.println("输出student的name:" + student.getStudentName());
System.out.println("---------------------");
System.out.println("输出student的card:" + student.getCard());
}
}
输出是:
输出student的name:张三
---------------------
创建了一个Card对象...
输出student的card:Card(id=2, idNumber=222222)
Card对象被创建时,会输出创建了一个Card对象...(见其类定义的构造器),通过输出可看出mapper.selectAll()执行时并没有创建Card对象。当调用了student的getCard()方法时才查询所需的Card对象。
通过idea的debug不能看出延迟加载的效果。debug过程中查看students变量可发现所有的card都被创建完成。。。

浙公网安备 33010602011771号