断断续续的阅读 mybatis 的源码有好几个月了,想把自己了解到的一些东西与大家分享。今天给大家分享一下 mybatis 的懒加载原理。

mybatis 的懒加载过程挺复杂的,涉及到的东西有很多,包括:配置文件的解析、mapper 文件的解析、sql 语句的映射、结果集的映射、懒加载等。

简单的说一下 mybatis 框架是干什么的

我用我自己的话来总结一下 mybatis 是干什么的,mybatis 不会直接和数据库进行打交道,mybatis 其实是对 jdbc api 的进一步封装,最终和数据库打交道的仍然是 jdbc 。

常用的 jdbc 操作有:增、删、改、查、事务等。在这里不准备讲事务,以后应该会专门写一篇博客讲 mybatis 事务的。

jdbc 的操作主要是通过 sql 语句来实现的。所以,mybatis 很大的一部分实现是怎样把 mybatis 的 xml(或注解)和参数一步一步最终映射为一条 sql 语句(或者是 sql 语句和执行这条 sql 语句所需的参数),然后把这条 sql 语句(或者是 这条sql 语句和执行这条 sql 语句所需的参数),然后再进一步的映射为 jdbc 的 Statement(或 PreparedStatement)。最终 jdbc 去执行 Statement(或 PreparedStatement)语句。

对于 增、删、改这三类操作,mybatis 的主要作用就是把 xml(或注解)和参数最终映射为 Statement(或 PreparedStatement),然后由 jdbc 去执行,最终映射为数据库中的一条记录或者是删除了数据库中的一条记录或者是修改了数据库中的一条记录。

查询操作要比前面的三类操作更复杂,因为查询操作首先是把 xml(或注解)和参数最终映射为 Statement(或 PreparedStatement),然后由 jdbc 去执行,最终返回一个结果集。然后 mybatis 再根据 xml 配置文件或者是接口方法中的返回值来判断返回给用户的数据类型是什么,根据返回类型再 new 出对象,然后将结果集中的值取出来,通过 Java 反射调用把值设置到对象中的一个过程。

把结果集映射为返回对象的过程是一个很复杂的过程,虽然原理看起来很简单,把结果集中的列字段映射为对象中的属性。因为 mybatis 的功能很强大,这些强大的功能的背后需要复杂的代码来实现。今天,在这里也不会讲将结果集映射为对象的过程,以后有时间可能会讲。

总结一下,mybatis 的最主要的两个功能:

  • 将 xml(或注解)和参数映射为 sql 交给 jdbc 去执行(增、删、改、查)
  • 对于查询,将 jdbc 查询出的结果集映射为对象,将映射好的对象返回给用户。

懒加载的例子

Blog.java:

public class Blog {private int id;private String content;private Author author;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Author getAuthor() {return author;}public void setAuthor(Author author) {this.author = author;}
}

Author.java:

public class Author {private int id;private 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;}
}

BlogMapper.xml:

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "shfq.lazy_load.vo.Blog"><select id="selectBlog" parameterType="int" resultMap="blogResultMap">SELECT * from blog where id=#{id}</select><resultMap id="blogResultMap" type="shfq.lazy_load.vo.Blog"><id column="id" property="id"></id><result column="content" property="content"></result><association property="author" column="author_id" select="selectAuthor" fetchType="lazy"/></resultMap><select id="selectAuthor" parameterType="int" resultType="shfq.lazy_load.vo.Author">SELECT * from author where id=#{id}</select>
</mapper>

mybatis-config.xml:

<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-demo"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="shfq/lazy_load/BlogMapper.xml"></mapper></mappers>
</configuration>

Test.java

public class Test {public static void main(String[] args) throws Exception {selectAuthor();}private static void selectAuthor() throws Exception {Reader reader = Resources.getResourceAsReader("shfq/lazy_load/mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);SqlSession session = sqlSessionFactory.openSession();Object o = session.selectOne("shfq.lazy_load.vo.Blog.selectBlog", 1);System.out.println("");}}

上面就是懒加载例子的相关代码,例子很简单。根据 id 查询 Blog,Blog 里又有一个 Author 对象,Author 对象是懒加载的。什么是懒加载?就是在需要它的时候才加载,不需要的话就不加载。

懒加载的原理

mybatis 会循环处理结果集中返回的每行数据的,在处理之前首先会通过反射调用构造方法来创建 result 对象,结果集中的一行数据最终会映射为一个 result 对象(严格的来说是不对的,结果集中的一行数据在多表连接的情况下可能会映射为多个 result 对象,结果集中的一行数据在多表连接一对多的情况下结果集中的多行数据可能映射一个 result 对象,简单的单表查询结果集中的一行数据映射为一个 result 对象)。

其实在调用构造方法创建 result 对象的时候,构造方法还可能会有参数,需要先把结果集中的参数都提取出来,传给相应的构造方法通过反射创建对象。

mybatis 其实第一步是解析配置文件,把配置文件映射为 mybatis 的 Configuration 类,把配置文件的 xml 属性都映射为 Java 对象中的属性值。xml mapper 文件的处理比较复杂,< resultMap/> 标签映射为 ResultMap 对象, 标签中的< id/> 、< result/>、< association/> 等映射为 ResultMapping 对象。

其实要讲 mybatis 的实现过程,涉及到的点太多了,没法在一篇博客中讲。在这里,我只简单的讲一下原理。

接着再讲创建好 result 对象之后,mybatis 会循环 resultMappings(标签中的每个子标签都映射为一个 resultMapping,这些 resultMapping 组成了一个集合就是 resultMappings)集合,看有没有需要懒加载的属性,如果有的话,则会为这个 result 对象创建一个代理对象。

什么情况下才会出现需要懒加载的属性呢?只有 < association property=”author” column=”author_id” select=”selectAuthor” fetchType=”lazy”/>
和 < collection property=”xxx” column=”xxx” select=”xxxx” fetchType=”lazy”/> 作为一个子标签出现在 < resultMap/> 标签(也不一定只是 < resultMap/> 标签,< collection/> 等标签事实上也算是一个 < resultMap/> 标签)之内才会出现需要懒加载的属性。select 属性和 fetchType=”lazy” 必须同时出现在 < collection/> 或 < association/> 属性中才需要懒加载,select 表示的是嵌套查询语句的 id ,fetchType=”lazy” 表示的是懒加载。

再接着讲 result 对象的代理对象,代理类是由 javassist 框架在运行时创建和加载的,这个代理类继承自 result 对象所属的类,以上面的例子为例,这个代理类继承自 Blog 类。

对于这个代理类的详细讲解在我的博客深入理解 Java 动态代理中有很详细的介绍,在这里我再简单的介绍下。

这个代理类继承了 result 对象所属的类(被代理类)并重写了被代理类的所有的方法,所以在代理对象上调用懒加载属性的 get 方法(getAuthor())时会触发懒加载动作,mybatis 这时就能发现需要去懒加载一个属性,然后去加载这个属性。

其实,有几个方法都可以触发懒加载的操作,比如调用懒加载的 get/set 方法(确实调用 set 方法时也会触发懒加载操作)还有调用 clone()、equals()、hashCode()、toString()方法也会触发懒加载操作,如果代理对象有多个懒加载属性,则调用后面的这四个方法时会同时触发加载所有的懒加载属性。

懒加载操作只会触发一次,下次再调用这些方法时不会再次触发懒加载操作的。

懒加载其实又是一次查询操作,懒加载查询需要传递一些参数,还有一些其他条件。这些待传递的属性、参数、查询所需的 sql 语句等相关的条件都已经封装到了代理对象内部,这些条件封装在一个 Map 中,键是懒加载查询的属性名称,值是查询该属性所需的条件,包括参数、sql 语句等。懒加载完一个属性之后会把这个属性从 Map 中移除,所以再次出发懒加载操作时 mybatis 就知道该属性已经被被加载过了,不会重复加载。

懒加载也是一个挺复杂的过程,我在上面的讲解中省略了很多,要全部讲出来涉及的东西太多。

我再总结一下,懒加载功能使用了代理对象,所以在调用懒加载属性的 get/set 方法(或者是其他触发懒加载操作的方法)时 mybatis 才能知道这时候应该去加载懒加载属性。

我对 mybatis 的懒加载原理已经很清楚了(我没有读过 hibernate 的源码,但我坚信 hibernate 的懒加载原理也是类似的,肯定使用了代理,所以才能知道需要在什么时候去加载懒加载属性),我在写出来的时候省略了许多过程,不知道大家能不能读懂。读懂我的 深入理解 Java 动态代理 这篇博客对理解 mybatis 的懒加载原理有很大的帮助,我建议大家认真读一下 深入理解 Java 动态代理 这篇博客。
读懂了我的 深入理解 Java 动态代理 这篇博客之后对于大家理解 AOP 原理、spring 事务管理(其他框架的事务管理也是类似的)等都会有很大的帮助。AOP、sprig 事务管理背后都用到了 Java 动态代理。

https://github.com/fengsmith/mybatis-3 这是我在阅读 mybatis 源码时 fork 的 mybatis 源码仓库,里面有我对源码的注释和阅读源码时构造的一些例子。阅读源码很不容易,需要坚持也需要方法,如果大家想要阅读 mybatis 源码的话可以与我交流。

mybatis 的懒加载原理相关推荐

  1. JPA/hibernate懒加载原理分析及JSON格式API反序列化时连环触发懒加载问题的解决

    什么是懒加载 JPA是java持久层的API,也就是java官方提供的一个ORM框架,Spring data jpa是spring基于hibernate开发的一个JPA框架.Spring data j ...

  2. lombok中的@Data注解与MyBatis的懒加载机制冲突解决

    使用@Data注解与mybatis的懒加载机制实现一对一关系查询时,发现怎么配置都无效,就是一下都查出来了,根本没有懒加载 1.application.yml配置文件配置如下: # mybatis 配 ...

  3. free mybatis 不生效_关于 Mybatis 设置懒加载无效的问题

    看了 mybatis 的教程,讲到关于mybatis 的懒加载的设置: 只需要在 mybatis 的配置文件中设置两个属性就可以了: 但是经过测试之后发现是无效的,经过一番折腾,发现是因为我在测试的时 ...

  4. js实现图片懒加载原理(marksheng)

    有时候一个网页会包含很多的图片,例如淘宝京东这些购物网站,商品图片多只之又多,页面图片多,加载的图片就多.服务器压力就会很大.不仅影响渲染速度还会浪费带宽.比如一个1M大小的图片,并发情况下,达到10 ...

  5. 深度剖析React懒加载原理

    目录 代码分割 React的懒加载 import() 原理 React.lazy 原理 Suspense 原理 参考 1.代码分割 (1)为什么要进行代码分割? 现在前端项目基本都采用打包技术,比如 ...

  6. vue 图片懒加载和懒加载原理

    在真实图片得到之前,展示懒加载设置的图片1.安装cnpm i vue-lazyload -S2.main.jsimport VueLazyload from 'vue-lazyload'Vue.use ...

  7. js实现图片懒加载原理

    有时候一个网页会包含很多的图片,例如淘宝京东这些购物网站,商品图片多只之又多,页面图片多,加载的图片就多.服务器压力就会很大.不仅影响渲染速度还会浪费带宽.比如一个1M大小的图片,并发情况下,达到10 ...

  8. 路由懒加载原理及使用

    懒加载解决的问题: 避免进入首页就加载全部的前端资源造成用户等待时间过长的问题. 就好比,访问 login 页面,你返回的 js 路由不仅有渲染 login 页面的,还有渲染 production 页 ...

  9. js图片懒加载原理、实现及节流优化

    1.懒加载原理 在图片没有进入可视区域时,先给的src一个默认加载的图片,这样浏览器就不会发送请求了,等到图片进入可视区域再把真实的图片路径data-src给src. 2.具体实现 1. 效果 2. ...

最新文章

  1. 英特尔蚕食AMD和NVIDIA?
  2. [置顶] PHP如何扩展和如何在linux底层对php扩展?
  3. BZOJ3123: [Sdoi2013]森林
  4. 怎么用python画饼状图_Python入门进阶:Python绘制饼图到Microsoft Excel
  5. Chromium OS 初体验
  6. 【lphtw】第三弹笔记ex24-ex35
  7. chapter5. Java数据结构与java算法
  8. 九宫格摆法_九张照片墙怎么摆放图,1的九宫格效果图
  9. 旷世科技面试——CV岗/后端开发
  10. 【源码】从Ansys导入mesh到MATLAB的小程序
  11. 英魂之刃后台用Java,《英魂之刃》系统操作说明
  12. 为什么选择嵌入式方向
  13. Android APP如何简单快速实现控制硬件设备并实现APP签名
  14. iphone复制不能全选_iphone长按键盘进行复制、粘贴、全选操作技巧
  15. 【营销获客一】消费金融大数据营销获客
  16. BCSP-玄子前端开发之JavaScript+jQuery入门CH13_表单校验
  17. Springboot毕设项目考勤打卡系统 k0e7rjava+VUE+Mybatis+Maven+Mysql+sprnig)
  18. ucloud如何新建一个虚拟机以及注意的地方
  19. Morse(摩斯电码)加解密实现(python)
  20. PHP获取IPV6地址

热门文章

  1. 中消协点评霸王条款:商品房合同警惕九大陷阱
  2. 签订认购书后开发商上涨房价这样合不合法
  3. 高德地图POI信息获取——爬虫小实验
  4. Linux 正则表达式练习(更新中)
  5. 更换硬盘或者U盘的盘符
  6. jar 打包解包一些命令
  7. Mac 下如何使用ll命令
  8. 点播, 单播,组播和广播
  9. MESI 和 NUMA
  10. XP 修复 ubuntu 启动