到了级联最后的章节,上次中我们讨论了延迟加载,但是最后有点凌乱,估计大家摸不着头脑,希望大家hold住,这里我们再升华为理论知识,就十分简单了。
1、树形层级加载:
上篇,我们将全局参数变为:
MyBatis级联探讨第一篇——概念和模型
的模型图,我们可以得到这样的一张树形图:
有了这个图,这样就方便论述了,
加载员工的时候,MyBatis会把员工和鉴别器,健康情况当做树形的第一层级加载,所以运行了2个SQL。
我们通过延迟加载员工卡信息的时候,因为负责项目信息和员工卡是同一个层级,所以MyBatis会连同该员工负责的项目也同时加载进来。
这便是MyBatis的树形加载。
2、非全局定义延迟加载策略:
在很多情况下,作为公司的管理层,往往对员工卡不感兴趣,而对其负责的项目情况则十分感兴趣,因此我们需要这样的一个场景,在加载员工信息的时候,顺便连同把其负责的项目信息也加载进来,而不需要把员工卡和健康情况这些信息加载进来。
但是按上篇要么全部延迟加载要么全部延迟加载,再或者是按树形加载。但是不要紧,mybatis为我们提供了可以非全局配置的延迟加载功能,让我们学习它们。
在关联的元素(association ,collection ,discriminator)中,我们存在一个属性:fetchType来决定是否需要延迟加载,如果配置它,它将覆盖掉原有在MyBatis设置的策略。
对于它而言,它有两个取值:
由它来决定是否需要延迟或者即刻加载。
这样让我们修改Mapper的配置,来保证加载员工信息的同事即刻加载项目信息,同时不要加载其他的信息,我们先把xml的配置修改过来:
请注意,关联的元素(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.
3、延迟加载的设计原理
延迟加载是使用动态代理来实现的,关于动态代理可以看到我的文章:
MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)
我们拿员工信息和员工卡信息作为例子谈谈,延迟加载。当我们获取了员工的信息,此时,我们生成一个员工卡的代理对象,而这个对象只是记录一些关键的关联字段,而没有发送SQL去把员工卡的信息拿出来,当我们调用员工卡的信息的时候,它就会进入动态代理的invoke方法,在访问信息前,通过SQL和记录的关联信息找到数据库的信息进行回填,这便是延迟加载的原理。
在MyBatis中,如果是在3.3.0版本(不含)以下,采用的是CGLib动态代理,以上(含)采用的是JAVASSIST。有关它们的研究可以参考别的书籍。
4、总结
好了,所有的我们都谈完了,都是笔者自己经过多次研究和反复的结果,如果有误欢迎指正,学到知识的,要感谢博主哦,写这些真的不是那么简单的,希望大家能够掌握MyBatis的级联哦。