天天看點

mybatis 懶加載

懶加載原理

懶加載是為改善,解析對象屬性時大量的嵌套子查詢的并發問題。設定懶加載後,隻有在使用指定屬性時才會加載,進而分散SQL請求。

通過對Bean的動态代理,重寫所有屬性的getXxx方法。在擷取屬性前先判斷屬性是否被加載,如果否則加載。

調用流程圖如下:

mybatis 懶加載

其中第一步的Bean代理過程發生在結果集解析 交建立對象之後(DefaultResultSetHandler.createResultObject),如果對應的屬性設定了懶加載,則會通過ProxyFactory 建立代理對象,該對象繼承自原對象,然後将對象的值全部拷貝到代理對像。并設定相應MethodHandler(原對象直接抛棄)

mybatis 懶加載

下面我們來看看代理後的結構

mybatis 懶加載

調試時的屬性值如下:

mybatis 懶加載

需要注意的是懶加載隻會觸發一次,因為執行懶加載的時候在map中移除了:

mybatis 懶加載

開啟方法

懶加載的開啟方式有以下幾種:

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” 均會觸發目前對象所有未執行的懶加載,源碼位置:

mybatis 懶加載

驗證幾種操作還會不會觸發懶加載

  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 懶加載

我們可以看到不能擷取到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();
    }
           
mybatis 懶加載

我們可以看到,在反序列化後,還是能調用成功,是因為在序列化過程中時,會通過實作Externalizable接口的writeExternal()和readExternal() 兩個方法将相關的環境變量設定和還原,使得懶加載能正常生效,相關源碼如下:

mybatis 懶加載

本文章的參考資料為:

https://www.coderead.cn/

繼續閱讀