详解MyBatis中懒加载功能的使用
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/69/ )。
在我们进行跨表数据查询的时候,常出现先查询表A、再根据表A的输出结果查询表B的情况。而有些时候,我们从A表中查询出来的数据中,只有部分需要查询B表。
例如我们需要从user表查询用户信息并打印所有用户的姓名列表。而查询出的用户中,只有满足“user.getAge() == 18”的用户才需要查询该用户在task表中的信息。这个过程如下所示。
User userParam = new User();
userParam.setSex(0);
// 查询满足条件的全部用户
List<User> userList = session.selectList("com.github.yeecode.mybatisdemo.dao.UserDao.lazyLoadQuery", userParam);
// 打印全部用户姓名列表
System.out.println("users: ");
for (User user : userList) {
System.out.println(user.getName() + ", age = " + user.getAge());
}
// 根据条件打印用户任务信息
System.out.println("userDetail: ");
for (User user : userList) {
if (user.getAge() == 18) {
System.out.println(user.getName() + ":");
for (Task task : user.getTaskList()) {
System.out.println(task.getTaskName());
}
}
}
我们可以先从user表获取用户信息,然后再从task表查询所有用户的任务信息。这一定是可行的,但是这样操作会查询出许多多余的结果,所有不满足“user.getAge() == 18”的用户的任务信息都是多余的。
一种更好的方案是先从user表获取用户信息,然后根据需要(即是否满足“user.getAge() == 18”)决定是否查询该用户在task表中的信息。
这种先加载必须的信息,然后再根据需要进一步加载信息的方式叫作懒加载。MyBatis便支持数据的懒加载。
要想使用懒加载,我们需要在MyBatis的配置文件中启用该功能,如下所示。
<settings>
<!--全局启用懒加载-->
<setting name="lazyLoadingEnabled" value="true" />
<!--激进懒加载设置 false即:懒加载时,每个属性都按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
aggressiveLazyLoading是激进懒加载设置,我们对该属性进行一些说明。当aggressiveLazyLoading设置为true时,对对象任一属性的读或者写操作都会触发该对象所有懒加载属性的加载;当aggressiveLazyLoading设置为false时,对对象某一懒加载属性的读操作会触发该属性的加载。无论aggressiveLazyLoading的设置如何,调用对象的"equals",“clone”,“hashCode”,“toString"中任意一个方法都会触发该对象所有懒加载属性加载。在源码中,我们会清晰地看到aggressiveLazyLoading设置项如何生效。
接下来我们还需要设置好映射文件,如下所示。
<resultMap id="associationUserMap" type="User">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="schoolName" column="schoolName"/>
<association property="taskList" javaType="ArrayList" select="com.github.yeecode.mybatisdemo.dao.UserDao.selectTask" column="id"/>
</resultMap>
<select id="lazyLoadQuery" resultMap="associationUserMap">
select * FROM `user` WHERE `sex` = #{sex}
</select>
<select id="selectTask" resultType="Task">
select * FROM `task` WHERE `userId` = #{id}
</select>
在“id="lazyLoadQuery"”的查询中,查询user表是必须的操作,而在结果的映射中又需要查询task表,因此它涉及到两个表的查询。而只要不访问User对象的taskList属性,则task表的查询操作就是可以省略的。因此,User对象的taskList就是可以懒加载的属性。
这样运行代码所示的查询操作,可以看到控制台打印出下图所示的输出。
可以看出MyBatis先从user表查询了所有的用户信息,然后仅对满足“user.getAge() == 18”的“易哥”调用了selectTask语句从task表查询了任务信息,而没有对不符合条件的“杰克”等人调用selectTask语句。因此,整个过程是存在懒加载的。
以上内容均参考《通用源码阅读指导书——MyBatis源码详解》一书。
这是一本以MyBatis的源码为实例讲述源码阅读方法的书籍,并且附带有示例项目源码,MyBatis的全中文注解。书籍还总结了大量的编程知识和架构经验,对提升编程和架构能力十分有用。
MyBatis懒加载的实现由executor包的loader子包支持,想了解其具体实现可以参考《通用源码阅读指导书——MyBatis源码详解》的“22.3 懒加载功能”部分,本文就不再展开了。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。
