一、延迟加载
延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。在mybatis中,resultMap标签 的association标签和collection标签具有延迟加载的功能。
1、需求
查询订单信息,关联查询用户信息。除了像之前的通过一个sql语句来完成查找,还可以
- 先创建一个statement来查询订单信息(查出来的有用户id)
- 然后创建一个statement来查询用户信息(根据已有id查询)
但是当暂时不需要用信息时可以先不执行后一句,这就是懒加载。
2、具体实现
首先创建查询订单信息的映射文件:
<!-- 延迟加载 -->
<select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
SELECT * FROM
orders
</select>
而它返回的结果集为:
<!-- lazyLoadingRstMap -->
<resultMap type="ordersExt" id="lazyLoadingRstMap">
<!-- 订单信息 -->
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<!-- 用户信息(一对一) -->
<association property="user"
select="com.shen.mybatis.mapper.UserMapper.findUserById" column="user_id"></association>
</resultMap>
说明:
- column:通过column指定的列所查询出的结果,作为select指的statement的入参,如果select指定的statement,入参需要多个值,需要在column中{col1=prop1,col2=prop2}。
- select:指定关联查询的查询statement(即查询用户的statement的id),然后将查询结果,封装到property属性指定的变量中。而查询用户的映射如下:
<!-- 根据用户ID查询用户信息 -->
<select id="findUserById" parameterType="int" resultType="User">
SELECT
* FROM USER WHERE id =#{id}
</select>
然后Mapper接口则为:
//延迟加载
public List<OrdersExt> findOrderAndUserLazyLoading();
然后需要在主配置文件中开启延迟加载:
<settings>
<!-- 开启延迟加载 ,默认值为true-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置积极的懒加载,默认是true -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
二、缓存
1、基本介绍
Mybatis的缓存,包括一级缓存和二级缓存。一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象;二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构。其结果图如下:
注意:一级缓存是默认使用的;二级缓存需要手动开启。
2、一级缓存
其原理如下:
2、二级缓存
其原理如下:
1.使用缓存
如果想要开启二级缓存,首先在全局配置文件中:
<settings>
<!-- 二级缓存的总开关 -->
<setting name="cacheEnabled" value="true"/>
</settings>
然后在想开启二级缓存的对应的映射文件中配置如下即可:
<!-- 开启二级缓存,默认使用了PerpetualCache -->
<cache />
但是如果只是设置成如上那样,如果缓存溢出将会报错,解决办法就是将对应的实体序列化(假如对应的实体为User):
public class User implements Serializable{
}
即可。
2.禁用缓存
如果某个查询语句不想使用缓存只要:
<select id="findUserById" parameterType="int" resultType="User" useCache="false">
SELECT
* FROM USER WHERE id =#{id}
</select>
3.刷新缓存
如果执行某个查询语句刷新缓存只要:
<select id="findUserById" parameterType="int" resultType="User" flushCache="true">
SELECT
* FROM USER WHERE id =#{id}
</select>
说明:flushCache的默认值在查询语句时是false,在增删改为true.
4.整合ehcache
Mybatis本身是一个持久层框架,它不是专门的缓存框架,所以它对缓存的实现不够好,不能支持分布式。Ehcache是一个分布式的缓存框架。
附加:分布式,系统为了提高性能,通常会对系统采用分布式部署(集群部署方式),结构如下:
Cache是一个接口,它的默认实现是mybatis的PerpetualCache。如果想整合mybatis的二级缓存,那么实现Cache接口即可。然后设置映射文件中cache标签的type值为ehcache的实现类:
<!-- 开启二级缓存,默认使用了PerpetualCache -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache" flushInterval="60000"/>
之后添加ehcache的配置文件(在config下,创建ehcache.xml),内容如下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 缓存数据要存放的磁盘地址 -->
<diskStore path="F:\develop\ehcache" />
<!-- diskStore:指定数据在磁盘中的存储位置。 defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的: maxElementsInMemory - 在内存中缓存的element的最大数目 maxElementsOnDisk
- 在磁盘上缓存的element的最大数目,若是0表示无穷大 eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: timeToIdleSeconds
- 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB
这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. diskPersistent
- 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 diskExpiryThreadIntervalSeconds
- 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 memoryStoreEvictionPolicy
- 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) -->
<defaultCache maxElementsInMemory="1000"
maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false"
timeToIdleSeconds="120" timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
5.应用场景
使用场景:对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。
注意:在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需求来设置,比如设置30分钟、60分钟等,单位为毫秒。
6.局限性
Mybatis二级缓存对细粒度的数据,缓存实现不好。
场景:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都是最新的商品信息,此时如果使用二级缓存,就无法实现当一个商品发生变化只刷新该商品的缓存信息而不刷新其他商品缓存信息,因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。
解决此类问题,需要在业务层根据需要对数据有针对性的缓存。
比如可以对经常变化的 数据操作单独放到另一个namespace的mapper中。