懒加载原理
懒加载是为改善,解析对象属性时大量的嵌套子查询的并发问题。设置懒加载后,只有在使用指定属性时才会加载,从而分散SQL请求。
通过对Bean的动态代理,重写所有属性的getXxx方法。在获取属性前先判断属性是否被加载,如果否则加载。
调用流程图如下:
其中第一步的Bean代理过程发生在结果集解析 交创建对象之后(DefaultResultSetHandler.createResultObject),如果对应的属性设置了懒加载,则会通过ProxyFactory 创建代理对象,该对象继承自原对象,然后将对象的值全部拷贝到代理对像。并设置相应MethodHandler(原对象直接抛弃)
下面我们来看看代理后的结构
调试时的属性值如下:
需要注意的是懒加载只会触发一次,因为执行懒加载的时候在map中移除了:
开启方法
懒加载的开启方式有以下几种:
lazyLoadingEnabled 全局懒加载开关 默认false
aggressiveLazyLoading 任意方法触发加载 默认false。
fetchType 加载方式 eager实时 lazy懒加载。默认eager
在调用对象配置了懒加载属性的的get方法,如:
<resultMap id="blogMap" type="com.entity.Blog" autoMapping="true">
<result column="title" property="title"></result>
<collection property="comments" column="id" select="selectCommentsByBlogId" fetchType="lazy">
</collection>
</resultMap>
调用了getComments会触发懒加载,
或者调用对象的"equals", “clone”, “hashCode”, “toString” 均会触发当前对象所有未执行的懒加载,源码位置:
验证几种操作还会不会触发懒加载
- 先调用配置了懒加载的set方法,再调用get方法的情况
@Test
public void lazySetTest(){
Blog blog=blogMapper.selectBlogById(1);
blog.setComments(new ArrayList<>());
for(Comment comment:blog.getComments()){
System.out.println(DateUtil.formatDate(comment.getDate())+"评论了本博客:"+comment.getContent());
}
System.out.println(blog.getComments());
}
我们可以看到不能获取到comments的记录,而正常情况下是可以的,说明被我们设置后的属性会使得懒加载失效
2、 序列化与反序列化的情况
<!--懒加载-->
<resultMap id="blogMap" type="com.entity.Blog" autoMapping="true">
<result column="title" property="title"></result>
<collection property="comments" column="id" select="selectCommentsByBlogId" fetchType="lazy">
</collection>
</resultMap>
@Before
public void init(){
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
druidDataSource.setValidationQuery("select 1");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, druidDataSource);
configuration = new Configuration(environment);
//测试懒加载序列化时需要自行设定一个configurationFactory的类。
configuration.setConfigurationFactory(LazyTest.ConfigurationFactory.class);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
this.session=sqlSessionFactory.openSession();
blogMapper=session.getMapper(BlogMapper.class);
}
/**
* 测试序列化和反序列化
*/
@Test
public void lasySerializableTest() throws IOException, ClassNotFoundException {
Blog blog=blogMapper.selectBlogById(1);
byte[] bytes=writeObject(blog);
Blog newBlog= (Blog) readObject(bytes);
System.out.println("反序列化完成");
newBlog.getComments();
}
private static byte[] writeObject(Object object) throws IOException {
ByteArrayOutputStream out=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(object);
return out.toByteArray();
}
private static Object readObject(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
我们可以看到,在反序列化后,还是能调用成功,是因为在序列化过程中时,会通过实现Externalizable接口的writeExternal()和readExternal() 两个方法将相关的环境变量设置和还原,使得懒加载能正常生效,相关源码如下:
本文章的参考资料为:
https://www.coderead.cn/