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

来源:互联网 发布:富士康工资知乎 编辑:程序博客网 时间:2024/05/21 10:21

微笑 到了级联最后的章节,上次中我们讨论了延迟加载,但是最后有点凌乱,估计大家摸不着头脑,希望大家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 ConnectionDEBUG 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: 2DEBUG 2016-05-16 14:15:55,347 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1DEBUG 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 0