文章目录
-
- MyBatis 延迟加载
-
- 1. 什么是延迟加载
- 2. 测试环境准备
- 3. 开启延迟加载
- 4. 总结
MyBatis 延迟加载
1. 什么是延迟加载
延迟加载也称为懒加载、惰性加载,使用延迟加载可以提高程序的运行效率,针对数据持久层的操作,在某些特定查询的情况下去访问特定的数据库,在其他情况下可以不访问某些数据表,尽量减少 SQL 的执行,从而达到提高速度的目的,是对数据库操作的一种优化。
说明:延迟加载只存在于数据表的级联查询中,单表查询没有延迟加载的功能。
具体的业务场景:数据库中有班级表(Class)和学生表(Student),当我们查询 Student 对象时,因为有级联关系,所以会将该 student 对象关联的 class 对象一并查询出来,这样就需要级联查询两张数据表的信息。
延迟加载的思路是:当我们查询 Student 对象时,如果只用到了 name 或 GPA 的字段信息,没有调用 class 属性,则只发送一条 SQL 语句查询 Student 表;如果需要调用 Class 的属性,则发送两条 SQL 语句查询 Student 和 Class。
2. 测试环境准备
Step1:创建 student 和 class 数据表
# student表
create table class
(
id int auto_increment primary key,
name varchar(22) null
);
# class表
create table student
(
id int auto_increment primary key,
name varchar(25) not null comment '姓名',
GPA double default 0 null comment '分数',
cid int null
);
Step2:创建 Student 和 Class 实体类
package com.training.entity;
import lombok.Data;
import java.util.List;
@Data
public class Class {
private Integer id;
private String name;
private List<Student> students;
}
package com.training.entity;
import lombok.Data;
@Data
public class Student {
private Integer id;
private String name;
private Double GPA;
private Class aClass;
}
Step3:在 config.xml 中添加配置信息
<?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>
<!--数据源-->
<environments default="dev">
<!--可以配置多个环境-->
<environment id="dev">
<!--配置JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- POOLED配置JDBC数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/training/mapper/StudentMapper.xml"></mapper>
<mapper resource="com/training/mapper/ClassMapper.xml"></mapper>
</mappers>
</configuration>
Step4:自定义 StudentMapper 接口
package com.training.mapper;
import com.training.entity.Student;
public interface StudentMapper {
public Student findById(Integer id);
}
Step5:SQL 配置文件 StudentMapper.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.training.mapper.StudentMapper">
<resultMap id="studentMap" type="com.training.entity.Student">
<id column="sid" property="id"></id>
<result column="sname" property="name"></result>
<result column="GPA" property="GPA"></result>
<association property="aClass" javaType="com.training.entity.Course">
<id column="cid" property="id"></id>
<result column="cname" property="name"></result>
</association>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="studentMap">
select s.id sid, s.name sname, GPA, c.id cid, c.name cname
from student s,class c
where s.cid = c.id and s.id = 1;
</select>
</mapper>
Step6:编写 Test 测试类
package com.training.test;
import com.training.entity.Class;
import com.training.entity.Student;
import com.training.entity.User;
import com.training.mapper.ClassMapper;
import com.training.mapper.CourseMapper;
import com.training.mapper.StudentMapper;
import com.training.mapper.UserMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;
public class Test {
public static void main(String[] args) {
//加载MyBatis配置信息
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
//构建SQLSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new
SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = factory.openSession();
//获取实现接口的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(mapper.findById(1));
}
}
在 config.xml 中配置 MyBatis 打印 SQL 设置
<settings>
<!-- 打印SQL -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
控制台的打印信息如下:
3. 开启延迟加载
在 config.xml 配置文件中开启延迟加载
<settings>
<!-- 打印SQL -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
将多表关联查询拆分成多个单表查询
1、自定义接口 StudentMapper.java
public interface StudentMapper {
public Student findById(Integer id);
}
2、SQL 配置文件 StudentMapper.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.training.mapper.StudentMapper">
<resultMap id="studentMapLazy" type="com.training.entity.Student">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="GPA" property="GPA"></result>
<association property="aClass" javaType="com.training.entity.Class" select="com.training.mapper.ClassMapper.findByClassId" column="cid">
</association>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="studentMapLazy">
select * from student where id=#{id};
</select>
</mapper>
3、自定义 ClassMapper.java
public interface ClassMapper {
public Class findByClassId(Integer id);
}
4、SQL 配置文件 ClassMapper.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.training.mapper.ClassMapper">
<resultMap id="classMapLazy" type="com.training.entity.Class">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
</resultMap>
<select id="findByClassId" parameterType="java.lang.Integer" resultMap="classMapLazy">
select * from class where id=#{id};
</select>
</mapper>
5、重新执行上面的测试类,控制台打印的结果如下:
同样的查询结果,但是 SQL 语句的执行方式不同,那么这样处理有什么特别的作用呢?
例1:根据学生的 id 查询 student 对象的 name 值。
public class Test {
public static void main(String[] args) {
//加载MyBatis配置信息
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
//构建SQLSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new
SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = factory.openSession();
//获取实现接口的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findById(1);
System.out.println(student.getName());
}
}
可以惊奇地发现,此时 SQL 语句只执行了一次。如果只需要获取 student 的信息,那么只执行第一条 SQL 就足够了。
但是如果需求是根据学生的 id 查询该学生的班级名 cname,即要获取 Class 表的级联信息,那么一条 SQL 语句就不够用了,此时需要再去执行第二条 SQL 语句,这就是按需加载。
public class Test {
public static void main(String[] args) {
//加载MyBatis配置信息
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
//构建SQLSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new
SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = factory.openSession();
//获取实现接口的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findById(1);
System.out.println(student.getAClass().getName());
}
}
4. 总结
MyBatis 框架的延迟加载,是实际开发中使用频率较高的功能,正确的使用延迟加载,可以有效减少 Java 程序与数据库交互次数,从而提升整个系统的运行效率,延迟加载适用于多表关联查询的业务场景,而单表查询本身只涉及到一张数据表的查询,所以也没有优化的余地了。