Mybatis的延迟加载
1、问题提出
现一个用户有多个地址,同时一个地址对应一个用户。
- 在查询用户信息时,要不要把关联地址查出来。
- 在查询地址信息时,要不要把关联用户查出来。
- 对于第一个问题,我们要确认什么时候需要用户的地址信息,没有必要每次都把地址信息查询出来。因为每次查询地址信息,对我们的开销是很大的,而且每次查询都不一定需要用到地址信息。
- 对于第二个问题,如果我们只是关心地址信息,而不知道地址信息是隶属于哪个用户,那么查出来也没有意义,所以每次查询地址信息时,都要带上用户信息。
2、延迟加载和立即加载
第一个问题就是延迟加载,第二个问题就是立即加载延迟加载:只有在使用到了对应的数据才进行查询,否则不进行查询。立即加载:不管是否使用到了,只要一调用方法,就会立马查询出来。
说明:表之间对应关系可以大致分为四种:一对一、一对多、多对一、多对多。
- 一对多、多对多通常情况下都是使用延迟加载。
- 一对一、多对一通常情况下都是使用立即加载。
3、实现一对一延迟加载案例演示
-
user_info表
-
address_info表
实体类
@Data
public class UserInfo {
private Integer id;
private String staffId;
private String staffName;
/**
* 逻辑删除
*/
private boolean enabled;
}
@Data
public class AddressInfo {
private Integer addressId;
private String userId;
private String city;
}
输出DTO对象
@Data
public class AddressInfoDto {
private Integer addressId;
private String userId;
private String city;
private UserInfo userInfo;
}
@Data
public class UserInfoDto {
private Integer id;
private String staffId;
private String staffName;
private boolean enabled;
private List<AddressInfo> addressInfo;
}
实现一对一延迟加载
核心配置文件
<settings>
<!--mybatis开启延迟加载,当开启时,所有关联对象都会延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启按需加载,当开启时,任何方法调用都会加载该对象的所有属性,否则,每个属性会按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
Mapper和映射文件
public interface AddressInfoMapper {
//查询所有地址信息
List<AddressInfoDto> findAllAddressInfo();
}
使用 association标签的select属性,指定查询地址信息的方法,后面加载地址的用户对象时,会根据配置方法路径,去调用方法执行查询用户信息。column是指定调用查询用户信息所需要的参数。
<?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.tencent.dao.mapper.AddressInfoMapper">
<resultMap id="BaseResultMapper" type="com.tencent.dao.entity.AddressInfo">
<id column="address_id" jdbcType="INTEGER" property="addressId"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="city" jdbcType="VARCHAR" property="city"/>
</resultMap>
<resultMap id="addressInfoDtoMap" type="com.tencent.dto.AddressInfoDto">
<id column="address_id" jdbcType="INTEGER" property="addressId"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="city" jdbcType="VARCHAR" property="city"/>
<association property="userInfo" column="user_id" javaType="com.tencent.dao.entity.UserInfo" select="com.tencent.dao.mapper.UserInfoMapper.findByStaffId">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="staff_id" jdbcType="VARCHAR" property="staffId"/>
<result column="staff_name" jdbcType="VARCHAR" property="staffName"/>
<result column="enbaled" jdbcType="" property="enabled"/>
</association>
</resultMap>
<!--查询所有地址信息-->
<select id="findAllAddressInfo" resultMap="addressInfoDtoMap">
select * from address_info
</select>
</mapper>
public interface UserInfoMapper {
//根据用户id查询用户信息
UserInfo findByStaffId(@Param("staffId") String staffId);
}
添加根据用户id查询用户信息方法。
<?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">
<!--指定映射文件对应的dao实体-->
<mapper namespace="com.tencent.dao.mapper.UserInfoMapper">
<resultMap id="BaseResultMapper" type="com.tencent.dao.entity.UserInfo">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="staff_id" jdbcType="VARCHAR" property="staffId"/>
<result column="staff_name" jdbcType="VARCHAR" property="staffName"/>
<result column="enabled" jdbcType="BIT" property="enabled"/>
</resultMap>
<!--根据用户id-->
<select id="findByStaffId" resultType="com.tencent.dao.entity.UserInfo">
select * from user_info where staff_id = #{staffId}
</select>
</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">
<!--mybatis的主配置文件-->
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--mybatis开启延迟加载,当开启时,所有关联对象都会延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启按需加载,当开启时,任何方法调用都会加载该对象的所有属性,否则,每个属性会按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--配置数据源信息 default指的是选择数据源-->
<environments default="mysql">
<environment id="mysql">
<!--配置事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/study-demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--配置映射文件mapper文件地址-->
<mappers>
<mapper resource="mapper/UserInfoMapper.xml"/>
<mapper resource="mapper/AddressInfoMapper.xml"/>
</mappers>
</configuration>
public class TestService {
private InputStream in = null;
private SqlSession sqlSession = null;
private UserInfoMapper userInfoMapper;
private AddressInfoMapper addressInfoMapper;
@Before
public void init() throws IOException {
//读取配置文件
in = Resources.getResourceAsStream("MybatisConfig.xml");
//创建工厂构建者
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//构建工厂
SqlSessionFactory factory = builder.build(in);
//根据工厂获取session
sqlSession = factory.openSession();
//sqlSession构建代理对象
userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);
addressInfoMapper = sqlSession.getMapper(AddressInfoMapper.class);
System.out.println();
}
@After
public void after(){
sqlSession.commit();
if(sqlSession != null){
sqlSession.close();
}
if(in!= null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test1(){
//测试一对一延迟加载
List<AddressInfoDto> allAddressInfo = addressInfoMapper.findAllAddressInfo();
for (AddressInfoDto addressInfo : allAddressInfo) {
System.out.println("地址信息"+addressInfo.getCity());
System.out.println("用户地址"+addressInfo.getUserId());
}
}
}
执行结果
将延迟加载打开后执行结果
<settings>
<!--mybatis开启延迟加载,当开启时,所有关联对象都会延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启按需加载,当开启时,任何方法调用都会加载该对象的所有属性,否则,每个属性会按需加载-->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
通过比较可以看出,当开启延迟加载,执行结果虽然相同,但是开启延迟加载后会在查看用户信息才会去数据库中进行查询,这样可以省去数据库的一下基本开销。
4、实现一对多延迟加载
实现一对多延迟加载是以查询用户信息为案例,一个用户对应多个地址信息,相关user_info的mapper文件如下
- 使用collection 标签的select属性,指定查询用户对应地址信息的方法,当加载用户地址信息时,会根据column属性所配置的参数,执行查询用户地址信息方法。
<?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.tencent.dao.mapper.UserInfoMapper">
<resultMap id="BaseResultMapper" type="com.tencent.dao.entity.UserInfo">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="staff_id" jdbcType="VARCHAR" property="staffId"/>
<result column="staff_name" jdbcType="VARCHAR" property="staffName"/>
<result column="enabled" jdbcType="BIT" property="enabled"/>
</resultMap>
<resultMap id="userInfoDtoMap" type="com.tencent.dto.UserInfoDto">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="staff_id" jdbcType="VARCHAR" property="staffId"/>
<result column="staff_name" jdbcType="VARCHAR" property="staffName"/>
<result column="enabled" jdbcType="BIT" property="enabled"/>
<collection property="addressInfo" column="staff_id" select="com.tencent.dao.mapper.AddressInfoMapper.findByUserId" ofType="com.tencent.dao.entity.AddressInfo"/>
</resultMap>
<select id="findAllUserInfo" resultMap="userInfoDtoMap">
select * from user_info
</select>
</mapper>
地址信息对应的映射mapper文件
- 提供一个根据用户id查询所有的地址信息方法
<?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.tencent.dao.mapper.AddressInfoMapper">
<resultMap id="BaseResultMapper" type="com.tencent.dao.entity.AddressInfo">
<id column="address_id" jdbcType="INTEGER" property="addressId"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="city" jdbcType="VARCHAR" property="city"/>
</resultMap>
<select id="findByUserId" resultType="com.tencent.dao.entity.AddressInfo">
select * from address_info where user_id = #{userId}
</select>
</mapper>
测试开启延迟加载执行结果
<settings>
<!--mybatis开启延迟加载,当开启时,所有关联对象都会延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启按需加载,当开启时,任何方法调用都会加载该对象的所有属性,否则,每个属性会按需加载-->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
可以看出,先执行了查询所有用户信息的sql语句,然后再挨个查询每个用户下的地址信息,实现了延迟加载。
总结
mybatis的延迟加载是通过 lazyLoadingEnabled 和 aggressiveLazyLoading 属性进行控制的,当一对一需要实现延迟加载时,需要打开延迟加载开关以及通过<association>标签的select 属性指定延迟加载对象查询的方法路径,column属性指定执行参数,在使用到其延迟加载对象时,mybatis才会进行查询。
当一对多时,通过<collection>标签的select属性指定延迟加载对象查询的方法路径,column属性指定执行参数,在使用到其延迟加载对象时,mybatis才会进行查询。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。













