Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。
题主你好,关于 MyBatis 创建代理对象用于延迟加载的实现,默认情况下并非使用 cglib 实现,而是使用 javassist,如果需要调整为 cglib 可以在配置中的 proxyFactory 进行设置,可以指定为 CGLIB 或 JAVASSIST,官网相关配置说明见 mybatis 配置。
另外从源码中也可以验证官网的说明。源码中用于创建代理对象的代码如下:
public class DefaultResultSetHandler implements ResultSetHandler {
/**
* 创建返回结果
*
* @param rsw 结果集
* @param resultMap 结果映射
* @param lazyLoader 结果的属性加载器 Map
* @param columnPrefix 列前缀
* @return
* @throws SQLException
*/
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
// 创建懒加载的代理,用于懒加载属性
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
}
这里的代理工厂 ProxyFactory 从配置 Configuration 中读取,Configuration 涉及 ProxyFactory 的代码如下。
public class Configuration {
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
public ProxyFactory getProxyFactory() {
return proxyFactory;
}
public void setProxyFactory(ProxyFactory proxyFactory) {
if (proxyFactory == null) {
proxyFactory = new JavassistProxyFactory();
}
this.proxyFactory = proxyFactory;
}
}
从配置 Configuration 中可以看出,默认的 ProxyFactory 是 JavassistProxyFactory。
如果需要调整,对于纯 mybatis 项目,可以在 xml 配置文件中指定。
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties>
<!--<property name="proxyFactory" value="JAVASSIST"/>-->
<property name="proxyFactory" value="CGLIB"/>
</properties>
</configuration>
mybatis 解析 proxyFactory 配置的代码如下。
public class XMLConfigBuilder extends BaseBuilder {
private void settingsElement(Properties props) {
... 省略部分代码
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
... 省略部分代码
}
}
对于技术的探索,最好还是持有怀疑态度,不要人云亦云。如果题主对自问自答这种形式感兴趣不妨写几篇博客。了解 mybatis 其他内容,还可以参阅我写的几篇关于 mybatis 的文章,专栏为 MyBatis 源码之旅。