缓存
一级缓存
默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库
一级缓存在下面情况会被清除
在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
在全局配置文件中设置 <setting name="localCacheScope" value="STATEMENT"/>,这样会使一级缓存失效,二级缓存不受影响
二级缓存
默认关闭,可通过全局配置文件中的<settings name="cacheEnabled" value="true"/>开启二级缓存总开关,然后在某个具体的mapper.xml中增加<cache />,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中
缓存的详细分析可以参考我之前的文章 => 极简mybatis缓存
关联查询
使用<resultMap> 标签以及<association>和<collection> 子标签,进行关联查询,比较简单,不多说
延迟加载
延迟加载是结合关联查询进行应用的。也就是说,只在<association>和<collection> 标签上起作用
对于关联查询,若不采用延迟加载策略,而是一次性将关联的从信息都查询出来,则在主信息比较多的情况下,会产生N+1问题,导致性能降低。比如用户信息和订单信息是一对多的关系,在查询用户信息时,设置了关联查询订单信息,如不采用延迟加载策略,假设共有100个用户,则我们查这100个用户的基本信息只需要一次SQL查询
select * from user;
若开启了关联查询,且不是延迟加载,则对于这100个用户,会发出100条SQL去查用户对应的订单信息,这样会造成不必要的性能开销(其实我认为称之为1+N问题更为合适)
select * from orders where u_id = 1; select * from orders where u_id = 2; .... select * from orders where u_id = 100;
当我们可能只关心id=3的用户的订单信息,则很多的关联信息是无用的,于是,采用延迟加载策略,可以按需加载从信息,在需要某个主信息对应的从信息时,再发送SQL去执行查询,而不是一次性全部查出来,这样能很好的提升性能。
另外,针对N+1问题,除了采用延迟加载的策略按需进行关联查询。如果在某些场景下,确实需要查询所有主信息关联的从信息。在上面的例子中,就是如果确实需要把这100个用户关联的订单信息全部查询出来,那怎么办呢?这里提供2个解决思路。
select * from user as u left join orders as o on u.id = o.u_id;
但使用连接查询查出来的结果是两表的笛卡尔积,还需要自行进行数据的分组处理
2是使用两个步骤来完成,先执行一条SQL,查出全部的用户信息,并把用户的id放在一个集合中,然后第二条SQL采用IN
关键字查询即可。这种方式也可以简化为子查询,如下
select * from orders where u_id in (select id from user);
现在说回来,mybatis的延迟加载默认是关闭的,可以通过全局配置文件中的<setting name="lazyLoadingEnabled" value="true"/>来开启,开启后,所有的SELECT查询,若有关联对象,都会采用延迟加载的策略。当然,也可以对指定的某个CRUD标签单独禁用延迟加载策略,通过设置SELECT标签中的fetchType=eager,则可以关闭该标签的延迟加载。
(还有一个侵入式延迟加载的概念,在配置文件中通过<setting name="aggressiveLazyLoading" value="true">来开启,大概是说,访问主对象中的主信息时,就会触发延迟加载,将从信息查询上来,这其实并不是真正意义的延迟加载,真正意义上的延迟加载应该是访问主对象中的从信息时,才触发延迟加载,去加载从信息,侵入式延迟加载默认是关闭的,一般情况下可以不用管他)
注意,延迟加载在关联查询的场景下才有意义。需要配合<resultMap>标签下的<association>和<collecction> 标签使用
<!-- StudentMapper.xml --> <resultMap id="studentExt" type="com.yogurt.po.StudentExt"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="score" column="score"/> <result property="age" column="age"/> <result property="gender" column="gender"/> <!-- 当延迟加载总开关开启时,resultMap下的association和collection标签中,若通过select属性指定嵌套查询的SQL,则其fetchType默认是lazy的,当在延迟加载总开关开启时,需要对个别的关联查询禁用延迟加载时,才有必要配置fetchType = eager --> <!-- column用于指定用于关联查询的列 property用于指定要封装到StudentExt中的哪个属性 javaType用于指定关联查询得到的对象 select用于指定关联查询时,调用的是哪一个DQL --> <association property="clazz" javaType="com.yogurt.po.Clazz" column="class_id" select="com.yogurt.mapper.ClassMapper.findById" fetchType="lazy"/> </resultMap> <select id="findLazy" parameterType="string" resultMap="studentExt"> SELECT * FROM student WHERE name like '%${value}%'; </select>
<!-- com.yogurt.mapper.ClassMapper --> <select id="findById" parameterType="int" resultType="com.yogurt.po.Clazz"> SELECT * FROM class WHERE id = #{id} </select>
/** 用于封装关联查询的对象 **/ public class StudentExt{ private Integer id; private String name; private Integer score; private Integer age; private Integer gender; /** 关联对象 **/ private Clazz clazz; //getter/setter }
标签:缓存,信息,查询,学习,MyBatis,日志,延迟,id,加载 From: https://www.cnblogs.com/yzx-sir/p/17224591.html