Mybatis的延迟加载

1、问题提出

现一个用户有多个地址,同时一个地址对应一个用户。

  1. 在查询用户信息时,要不要把关联地址查出来。
  2. 在查询地址信息时,要不要把关联用户查出来。
  • 对于第一个问题,我们要确认什么时候需要用户的地址信息,没有必要每次都把地址信息查询出来。因为每次查询地址信息,对我们的开销是很大的,而且每次查询都不一定需要用到地址信息。
  • 对于第二个问题,如果我们只是关心地址信息,而不知道地址信息是隶属于哪个用户,那么查出来也没有意义,所以每次查询地址信息时,都要带上用户信息。

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的延迟加载是通过 lazyLoadingEnabledaggressiveLazyLoading 属性进行控制的,当一对一需要实现延迟加载时,需要打开延迟加载开关以及通过<association>标签的select 属性指定延迟加载对象查询的方法路径,column属性指定执行参数,在使用到其延迟加载对象时,mybatis才会进行查询。

当一对多时,通过<collection>标签的select属性指定延迟加载对象查询的方法路径,column属性指定执行参数,在使用到其延迟加载对象时,mybatis才会进行查询。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章