MyBatis级联第五篇——树形加载规则和非全局性加载,延迟加载原理(推荐,继续上篇的重要原创)

微笑 到了级联最后的章节,上次中我们讨论了延迟加载,但是最后有点凌乱,估计大家摸不着头脑,希望大家hold住,这里我们再升华为理论知识,就十分简单了。

1、树形层级加载:

上篇,我们将全局参数变为:

<setting name="lazyLoadingEnabled" value="true"/>  
     <setting name="aggressiveLazyLoading" value="true"/>  

我们发现其延迟加载规则,貌似不可触摸,于是大家都很懵懂,不过不要紧我们我们回到第一篇

MyBatis级联探讨第一篇——概念和模型

的模型图,我们可以得到这样的一张树形图:

有了这个图,这样就方便论述了,

加载员工的时候,MyBatis会把员工和鉴别器,健康情况当做树形的第一层级加载,所以运行了2个SQL。

我们通过延迟加载员工卡信息的时候,因为负责项目信息和员工卡是同一个层级,所以MyBatis会连同该员工负责的项目也同时加载进来。

这便是MyBatis的树形加载。


2、非全局定义延迟加载策略:


在很多情况下,作为公司的管理层,往往对员工卡不感兴趣,而对其负责的项目情况则十分感兴趣,因此我们需要这样的一个场景,在加载员工信息的时候,顺便连同把其负责的项目信息也加载进来,而不需要把员工卡和健康情况这些信息加载进来。

但是按上篇要么全部延迟加载要么全部延迟加载,再或者是按树形加载。但是不要紧,mybatis为我们提供了可以非全局配置的延迟加载功能,让我们学习它们。

在关联的元素(association  ,collection  ,discriminator)中,我们存在一个属性:fetchType来决定是否需要延迟加载,如果配置它,它将覆盖掉原有在MyBatis设置的策略。

对于它而言,它有两个取值:

  • lazy: 延迟加载
  • eager:即刻加载

由它来决定是否需要延迟或者即刻加载。

这样让我们修改Mapper的配置,来保证加载员工信息的同事即刻加载项目信息,同时不要加载其他的信息,我们先把xml的配置修改过来:

<settings>
     <setting name="cacheEnabled" value="true"/>
     <setting name="lazyLoadingEnabled" value="true"/>
     <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

然后加入如下的配置:

<?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.ykzhen2015.csdn.mapper.EmployeeMapper">
	<resultMap id="employeeMap" type="com.ykzhen2015.csdn.pojo.Employee">
		<id property="id" column="id" />
		<result property="empName" column="emp_name" />
		<result property="sex" column="sex" />
		<association property="employeeCard" column="id" fetchType="lazy"
			select="com.ykzhen2015.csdn.mapper.EmployeeCardMapper.getEmployeeCardByEmpId" />
		<collection property="projectList" column="id" fetchType="eager"
			select="com.ykzhen2015.csdn.mapper.ProjectMapper.findProjectByEmpId" />
		<discriminator javaType="int" column="sex">
			<case value="1" resultMap="maleEmployeeMap" />
			<case value="2" resultMap="femaleEmployeeMap" />
		</discriminator>
	</resultMap>

	<select id="getEmployee" parameterType="int" resultMap="employeeMap">
		select id, emp_name as empName, sex from t_employee where id =#{id}
	</select>

	<resultMap id="maleEmployeeMap" type="com.ykzhen2015.csdn.pojo.MaleEmployee" extends="employeeMap">
		<collection fetchType="lazy" property="prostateList" select="com.ykzhen2015.csdn.mapper.MaleEmployeeMapper.findProstateList" column="id" />
	</resultMap>

	<resultMap id="femaleEmployeeMap" type="com.ykzhen2015.csdn.pojo.FemaleEmployee" extends="employeeMap">
		<collection fetchType="lazy" property="uterusList" select="com.ykzhen2015.csdn.mapper.FemaleEmployeeMapper.findUterusList" column="id" />
	</resultMap>
</mapper>


请注意,关联的元素(association  ,collection  ,discriminator)中的fetchType属性,它的范围是:eager和lazy。我们这里将取员工卡和健康情况的设置为lazy,意味着它将延迟加载,把获取项目情况的设置为eager,意味着加载员工信息后,就会加载项目情况,好让我们再次运行程序,

public static void main(String []args) {
		SqlSession sqlSession = null;
		try {
			sqlSession = SqlSessionFactoryUtil.openSqlSession();
			EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
			Employee emp = employeeMapper.getEmployee(1);
		} finally {
			if (sqlSession != null) {
			    sqlSession.close();
			}
		}
	}


可以得到如下日志:

DEBUG 2016-05-16 14:15:54,793 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG 2016-05-16 14:15:54,883 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-05-16 14:15:54,883 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-05-16 14:15:54,883 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-05-16 14:15:54,883 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-05-16 14:15:54,883 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-05-16 14:15:54,993 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-05-16 14:15:55,227 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1037324811.
DEBUG 2016-05-16 14:15:55,227 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select id, emp_name as empName, sex from t_employee where id =? 
DEBUG 2016-05-16 14:15:55,259 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====>  Preparing: SELECT a.id, a.proj_name as projName FROM t_project a, t_employee_project b where a.id = b.proj_id and b.emp_id = ? 
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====> Parameters: 1(Integer)
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <====      Total: 2
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 1037324811 to pool.

好,它打印了我们运行的轨迹,正是我们所需要的,在加载员工信息后,它会马上加载项目信息,而对于其他的一律都不予加载,通过fetchType属性,我们可以自定义那些可以加载,那些不加载,这样就可以很灵活的处理级联的延迟加载问题。


3、延迟加载的设计原理

延迟加载是使用动态代理来实现的,关于动态代理可以看到我的文章:

MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)

我们拿员工信息和员工卡信息作为例子谈谈,延迟加载。当我们获取了员工的信息,此时,我们生成一个员工卡的代理对象,而这个对象只是记录一些关键的关联字段,而没有发送SQL去把员工卡的信息拿出来,当我们调用员工卡的信息的时候,它就会进入动态代理的invoke方法,在访问信息前,通过SQL和记录的关联信息找到数据库的信息进行回填,这便是延迟加载的原理。

在MyBatis中,如果是在3.3.0版本(不含)以下,采用的是CGLib动态代理,以上(含)采用的是JAVASSIST。有关它们的研究可以参考别的书籍。


4、总结

好了,所有的我们都谈完了,都是笔者自己经过多次研究和反复的结果,如果有误欢迎指正,学到知识的,要感谢博主哦,写这些真的不是那么简单的,希望大家能够掌握MyBatis的级联哦大笑



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值