上一篇部分阅读了Mybatis的源码梳理了一下它作为ORM层解决方案是如何帮助我们访问数据库的,今天想再总结一下Mybatis的两个特性。
Mybatis缓存机制分为一级缓存和二级缓存。
一级缓存是sqlSession级的,即利用同一个sqlSession执行查询相同的SQL,数据会直接从缓存中取。我们都知道一个sqlSession会对应new一个调度器Excutor对象,每次Excutor去执行SQL前都会去缓存里看一下是不是之前执行过了,当然了本质上就是从一个Map里面找key的操作,执行过了就直接去缓存里取没有就访问数据库。Mybatis是默认开启一级缓存的。
二级缓存是Mapper级别的,可以这么理解,只要是执行了同一个mapper.xml中的同一个SQL,都会直接从缓存里取,不管是不是同一个sqlSession。二级缓存需要去自己开启,两个方法一是可以在想缓存作用域的mapper.xml中写
也可以在配置文件里配置cacheEnabled=true,如果开启了二级缓存,查询数据的循序为二级缓存--->一级缓存--->数据库。即可以二级缓存和一级缓存同时开启,不存在选择一个的关系。
Mybatis的延迟加载其实很实用,我们查询数据库的时候经常会去左关联,左关联嘛,左表才是主表,关联表的数据不一定需要立刻查询出来。举个例子,查询人员的出入记录时候关联了人员表查询该人员的详细信息,出入记录表是主表,页面上直接展示的只有该人员的出入记录信息,直接展示的内容并不需要该人员详细信息,要点击比如查看人员详情,这时候才需要这个人的详细信息,即这个时候才需要关联表人员表里的数据。相同的场景还有查询订单信息不一定立刻需要改商品的详细信息,很多,这个时候延迟加载就有登场了,即延迟数据的加载时机。
怎么理解延迟加载呢?还是用上面人员出入记录的例子,在只访问了一次数据库的前提下,先只加载出入记录信息,当需要人员详细信息的时候再加载人员详细信息,什么时候需要呢?比如点了一下人员详情。当然这是在一次访问数据库的前提下,这个逻辑分两次访问数据库很容易做到,这就不说了。
延迟加载在配置文件中用lazyLoadingEnabled=true开启,默认是关闭的,说到延迟加载,不得不说另一个按需加载,这个按需加载是默认开启的,用aggressiveLazyLoading=false去关掉它。
这两个概念有点容易混淆,但是他们可是一对冤家,反着来的,粘贴了一篇博客里的例子看一下,原文网址https://www.cnblogs.com/ashleyboy/p/9286814.html
mapper.xml是这样的,可以看出resultMap有子结果集即子查询。
当前配置:启用延迟加载lazyLoadingEnabled=true和按需加载aggressiveLazyLoading=false
Step1: 执行Mapper方法lazyLoadTest,实际只执行了select * from category where id=#{id}
Step2: 执行System.out.println(category.getName()),加载属性,由于启用按需加载aggressiveLazyLoading=false,name属性此前已加载好,所以此处无数据库交互
Step3: 执行category.getProductList().size(),加载属性productList,按需加载需要执行延迟加载sql脚本select * from products where categoryid=#{id},与数据库交互,效果如图:
将aggressiveLazyLoading属性设为为true,即:
再一下执行过程:
此时的配置:启用延迟加载lazyLoadingEnabled=true和关闭按需加载aggressiveLazyLoading=false(即加载对象则加载所有属性)
Step1: 执行Mapper方法lazyLoadTest,实际只执行了select * from category where id=#{id}
Step2: 执行System.out.println(category.getName()),访问name属性,由于启用按需加载aggressiveLazyLoading=true,关闭按需加载,则加载category对象时加载该对象的所有属性,执行延迟加载sql脚本select * from products where categoryid=#{id},与数据库交互
Step3: 执行category.getProductList().size(),访问productList属性,该 性step2已加载好,此处无需数据库交互
对比上下两个配置的执行情况可以发现 aggressiveLazyLoading和lazyLoadingEnabled是由多冤家。按需加载的意思是只要是resultMap里需要的都会一次全部加载出来,子结果集的查询SQL也会被执行,一想我们平时不就是这样嘛,原来是因为按需加载默认开启的。所以要想用延迟加载,必须先关闭按需加载,这样resultMap结果集返回的其实没有子结果集的数据,因为没有执行子结果集的查询SQL,只有编码里用Category.getProductList()时候想要获取子结果集的数据时才会去执行子结果集的查询SQL。这样控制一次返回的数据量就降低了数据库的压力。