博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis源代码分析:深入了解mybatis延迟加载机制
阅读量:5235 次
发布时间:2019-06-14

本文共 8472 字,大约阅读时间需要 28 分钟。

下文从mybatis( )延迟加载样例讲起,逐步深入其实现机制。

下面的例子是Student类关联一个Teacher对象,在访问Student对象时,不立即加载其关联的Teacher对象,而是等到访问Teacher对象的属性时,才加载Teacher对象。

源代码下载:

1.Student.java

package dao.domain;public class Student {	public int id;	public String name;	public int teacher_id;	public Teacher teacher;	public int getId() {		return id;	}	public void setId(int id) {		this.id = id;	}	public String getName() {		return name;	}	public void setName(String name) {		this.name = name;	}	public int getTeacher_id() {		return teacher_id;	}	public void setTeacher_id(int teacher_id) {		this.teacher_id = teacher_id;	}	public Teacher getTeacher() {		return teacher;	}	public void setTeacher(Teacher teacher) {		this.teacher = teacher;	}}

2.Teacher.java

package dao.domain;public class Teacher {	public int id;	public String name;	public int getId() {		return id;	}	public void setId(int id) {		this.id = id;	}	public String getName() {		return name;	}	public void setName(String name) {		this.name = name;	}		}

3.StudentDAO.java

package dao;import dao.domain.Student;public interface StudentDAO {	public Student getStudentByID(int id);}

4.StudentDAOImpl.java

package dao.impl;import java.io.IOException;import java.io.Reader;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import dao.StudentDAO;import dao.domain.Student;public class StudentDaoImpl implements StudentDAO {	private static SqlSession session = null;	private static StudentDAO mapper = null;		static{		String resouce = "config/ibatisConfiguration.xml";		Reader reader = null;		try {			reader = Resources.getResourceAsReader(resouce);		} catch (IOException e) {			e.printStackTrace();		}				SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);		session = factory.openSession();		mapper = session.getMapper(StudentDAO.class);				try {			reader.close();		} catch (IOException e) {			e.printStackTrace();		}	}	@Override	public Student getStudentByID(int id) {		return mapper.getStudentByID(id);	}	}

5.TeacherMapper.xml

6.StudentMapper.xml

7.ibatisConfiguration.xml

8.测试类

import dao.StudentDAO;import dao.domain.Student;import dao.impl.StudentDaoImpl;public class Test {	public static void main(String[] args) {		StudentDAO dao = new StudentDaoImpl();		Student article = dao.getStudentByID(1);				System.out.println(article.getTeacher().getName());	}}

9.mybatis延迟加载说明:

1)在mybatis配置文件中,配置如下两个配置项:

2)查询语句样例,下面通过association将Student对象一个属性与一个查询语句关联起来:

这样,在查询出StudentMap这个结果集时,对于teacher字段就会采取延迟加载了,等到访问teacher对象属性时,才会去加载关联的teacher对象。

10.逐步深入延迟加载细节

1)在Test类中如下行打上断点,然后进行Debug

System.out.println(article.getTeacher().getName());

可以看到article对象为(每次测试,结果会不同):

这就是一个cglib自动生成的代理对象。

2)那这个代理对象什么时候生成的呢? 经过反复不停的debug跟踪,得到下面这样一个调用栈。

Thread [main] (Suspended (breakpoint at line 61 in CglibProxyFactory))		CglibProxyFactory.createProxy(Object, ResultLoaderMap, Configuration, ObjectFactory, List
>, List
) line: 61 DefaultResultSetHandler.createResultObject(ResultSetWrapper, ResultMap, ResultLoaderMap, String) line: 512 DefaultResultSetHandler.getRowValue(ResultSetWrapper, ResultMap) line: 331 DefaultResultSetHandler.handleRowValuesForSimpleResultMap(ResultSetWrapper, ResultMap, ResultHandler, RowBounds, ResultMapping) line: 291 DefaultResultSetHandler.handleRowValues(ResultSetWrapper, ResultMap, ResultHandler, RowBounds, ResultMapping) line: 266 DefaultResultSetHandler.handleResultSet(ResultSetWrapper, ResultMap, List, ResultMapping) line: 236 DefaultResultSetHandler.handleResultSets(Statement) line: 150 PreparedStatementHandler.query(Statement, ResultHandler) line: 60 RoutingStatementHandler.query(Statement, ResultHandler) line: 73 SimpleExecutor.doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) line: 60 SimpleExecutor(BaseExecutor).queryFromDatabase(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) line: 267 SimpleExecutor(BaseExecutor).query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) line: 137 CachingExecutor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) line: 96 CachingExecutor.query(MappedStatement, Object, RowBounds, ResultHandler) line: 77 DefaultSqlSession.selectList(String, Object, RowBounds) line: 108 DefaultSqlSession.selectList(String, Object) line: 102 DefaultSqlSession.selectOne(String, Object) line: 66 MapperMethod.execute(SqlSession, Object[]) line: 68 MapperProxy
.invoke(Object, Method, Object[]) line: 52 $Proxy0.getStudentByID(int) line: not available StudentDaoImpl.getStudentByID(int) line: 40 Test.main(String[]) line: 12

可以看到在查询Student对象时,DefaultResultSetHandler(org\apache\ibatis\executor\resultset)类会对数据库查询的每一行结果进行封装处理。

通过下面的代码可以看得更清楚,当结果对象中存在嵌套查询时,当前对象就会被替换为一个代理对象。

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {    final List
> constructorArgTypes = new ArrayList
>(); final List
constructorArgs = new ArrayList(); final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { final List
propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149 return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); } } } return resultObject; }

3)那当访问代理对象时,会发生什么事情呢?

如调用System.out.println(article.getTeacher().getName())时,得到如下的调用栈信息

Thread [main] (Suspended (breakpoint at line 143 in CglibProxyFactory$EnhancedResultObjectProxyImpl))		owns: ResultLoaderMap  (id=38)		CglibProxyFactory$EnhancedResultObjectProxyImpl.intercept(Object, Method, Object[], MethodProxy) line: 143		Student$$EnhancerByCGLIB$$e1afda28.getTeacher() line: not available		Test.main(String[]) line: 10

代码执行到了如下地方:

if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {              if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {                lazyLoader.loadAll();              } else if (PropertyNamer.isProperty(methodName)) {                final String property = PropertyNamer.methodToProperty(methodName);                if (lazyLoader.hasLoader(property)) {                  lazyLoader.load(property);                }              }            }

通过调用lazyLoader.loadAll来对需要加载的对象进行加载.

继续顺藤摸瓜,得到如下的调用栈:

Thread [main] (Suspended)		owns: ResultLoaderMap  (id=38)		BeanWrapper.set(PropertyTokenizer, Object) line: 57		MetaObject.setValue(String, Object) line: 133		ResultLoaderMap$LoadPair.load(Object) line: 207		ResultLoaderMap$LoadPair.load() line: 172		ResultLoaderMap.load(String) line: 80		ResultLoaderMap.loadAll() line: 90		CglibProxyFactory$EnhancedResultObjectProxyImpl.intercept(Object, Method, Object[], MethodProxy) line: 143		Student$$EnhancerByCGLIB$$e1afda28.getTeacher() line: not available		Test.main(String[]) line: 10

如下是BeanWrapper中setValue方法:

public void set(PropertyTokenizer prop, Object value) {    if (prop.getIndex() != null) {      Object collection = resolveCollection(prop, object);      setCollectionValue(prop, collection, value);    } else {      setBeanProperty(prop, object, value);    }  }

通过setBeanProperty方法,就完成向Student对象设置Teacher属性的过程(上面代码中的object就是Student对象)。

下面通过一张图,展示一下代理对象与实际对象之间的关系,”为代理对象,是Student类的子类,当访问到Student的getTeacher方法时,其通过关联的EnhancedResultObjectProxyImpl类来加载teacher对象,具体关联见下图,其中BeanWrapper中object就是代理对象本身,通过对object的设置,将teacher属性设置为从数据库查询出来的对象,完成延迟加载。

至此,mybatis的整个延迟加载过程就分析完了。

参考链接:
mybatis下载主页: 






转载于:https://www.cnblogs.com/jerry1999/p/3740150.html

你可能感兴趣的文章
设计模式之桥接模式(Bridge)
查看>>
jquery的$(document).ready()和onload的加载顺序
查看>>
Python Web框架Django (五)
查看>>
.net学习之继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串------(转)...
查看>>
【codevs1033】 蚯蚓的游戏问题
查看>>
【程序执行原理】
查看>>
python的多行注释
查看>>
连接Oracle需要jar包和javadoc文档的下载
查看>>
UVA 10976 - Fractions Again?!
查看>>
Dreamweaver cc新版本css单行显示
查看>>
【android】安卓的权限提示及版本相关
查看>>
JavaScript可否多线程? 深入理解JavaScript定时机制
查看>>
IOS基础学习
查看>>
PHP 导出 Excell
查看>>
Java基础教程——网络基础知识
查看>>
自己到底要的是什么
查看>>
Kruskal基础最小生成树
查看>>
ubuntu 14.04 安装搜狗拼音输入法
查看>>
浅谈算法和数据结构: 一 栈和队列
查看>>
Java内部类详解
查看>>