mybatis 懒加载

懒加载原理

懒加载是为改善,解析对象属性时大量的嵌套子查询的并发问题。设置懒加载后,只有在使用指定属性时才会加载,从而分散SQL请求。
通过对Bean的动态代理,重写所有属性的getXxx方法。在获取属性前先判断属性是否被加载,如果否则加载。
调用流程图如下:
mybatis 懒加载_第1张图片

其中第一步的Bean代理过程发生在结果集解析 交创建对象之后(DefaultResultSetHandler.createResultObject),如果对应的属性设置了懒加载,则会通过ProxyFactory 创建代理对象,该对象继承自原对象,然后将对象的值全部拷贝到代理对像。并设置相应MethodHandler(原对象直接抛弃)

mybatis 懒加载_第2张图片

下面我们来看看代理后的结构mybatis 懒加载_第3张图片

调试时的属性值如下:
mybatis 懒加载_第4张图片

需要注意的是懒加载只会触发一次,因为执行懒加载的时候在map中移除了:
mybatis 懒加载_第5张图片

开启方法

懒加载的开启方式有以下几种:
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” 均会触发当前对象所有未执行的懒加载,源码位置:
在这里插入图片描述

验证几种操作还会不会触发懒加载

  1. 先调用配置了懒加载的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());
    }

mybatis 懒加载_第6张图片
我们可以看到不能获取到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() 两个方法将相关的环境变量设置和还原,使得懒加载能正常生效,相关源码如下:
mybatis 懒加载_第7张图片

本文章的参考资料为:
https://www.coderead.cn/

你可能感兴趣的:(mybatis源码,mybatis)