一、mybatis延迟加载
1、延迟加载概述
(1)延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息
(2)在mybatis中,resultMap标签 的association标签和collection标签具有延迟加载的功能。
2、延迟加载案例分析
(1)需求:查询订单信息,关联查询用户信息
a、创建一个statement来查询订单信息
b、创建一个statement来查询用户信息
(2)映射文件:
<!-- lazyLoadingRstMap -->
<resultMap type="ordersExt" id="lazyLoadingRstMap">
<!-- 订单信息 -->
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<!-- 用户信息(一对一) -->
<!-- select:指定关联查询的查询statement(即查询用户的statement的id),然后将查询结果,封装到property属性指定的变量中 -->
<!-- column:通过column指定的列所查询出的结果,作为select指的statement的入参 -->
<!-- 注意:如果select指定的statement,入参需要多个值,需要在column中{col1=prop1,col2=prop2} -->
<association property="user"
select="com.san.mapper.UserMapper.findUserById" column="user_id"></association>
</resultMap>
<!-- 延迟加载 -->
<select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
SELECT * FROM
orders
</select>
创建查询用户信息的映射文件:
<!-- 根据用户ID查询用户信息 -->
<select id="findUserById" parameterType="int" resultType="com.san.po.User">
SELECT
* FROM USER WHERE id =#{id}
</select>
(3)mapper接口:
//延迟加载
public List<OrdersExt> findOrderAndUserLazyLoading();
(4)测试代码:
//懒加载
@Test
public void Test01() throws IOException{
// 读取配置文件
// 全局配置文件的路径
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建OrdersMapper对象
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersExt> list = mapper.findOrderAndUserLazyLoading();
//按需加载时,是需要的时候再去查询数据库
for (OrdersExt ordersExt : list) {
System.out.println(ordersExt.getUser());
}
sqlSession.close();
}
(5)设置延迟加载:
在SqlMapConfig.xml中,配置settings标签
<settings>
<!-- 开启延迟加载 ,默认值为true-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置积极的懒加载,默认是true -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 二级缓存的总开关 -->
<setting name="cacheEnabled" value="true"/>
</settings>
二、mybatis一级缓存
1、mybatis缓存理解
(1)Mybatis的缓存,包括一级缓存和二级缓存,一级缓存是默认使用的,二级缓存需要手动开启。
(2)一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
(3)二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
(4)图解:
2、一级缓存原理
3、一级缓存测试
(1)测试1:
@Test
//一级缓存测试1
public void Test01() throws Exception{
//全局配置文件
String resource="SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建Mapper对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询
User user1=userMapper.findUserById(1);
System.out.println(user1);
//第二次查询
User user2=userMapper.findUserById(1);
System.out.println(user2);
//关闭资源
sqlSession.close();
}
(2)测试2:
@Test
//一级缓存测试2
public void Test02() throws Exception{
//全局配置文件
String resource="SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建mapper对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//第一次查询
User user1=mapper.findUserById(1);
System.out.println(user1);
//执行添加用户操作
mapper.insertUser(user1);
//执行commit操作,将一级缓存清空
sqlSession.commit();
//第二次查询
User user2=mapper.findUserById(1);
System.out.println(user2);
}
(3)解释:
三、mybatis二级缓存
1、二级缓存原理
2、二级缓存测试
(1)开启二级缓存:
a、开启二级缓存的总开关
<!-- 二级缓存的总开关 -->
<setting name="cacheEnabled" value="true"/>
b、在mapper映射文件中开启二级缓存
<!-- 开启二级缓存,默认使用PerpetualCache -->
<cache/>
(2)序列化:
二级缓存,需要序列化是因为需要将缓存的数据写入硬盘
public class User implements Serializable
(3)测试1:
@Test
//二级缓存测试1
public void Test01() throws Exception{
String resource="SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
//由mybatis通过sqlSession来创建代理对象
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
//第一次查询
User user1=mapper1.findUserById(1);
System.out.println(user1);
//在close的时候,才会将数据写入二级缓存中
sqlSession1.close();
//第二次查询
User user2=mapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}
(4)测试2:
@Test
//二级缓存测试2
public void Test02() throws Exception{
String resource="SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
//由mybatis通过sqlSession来创建代理对象
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
//第一次查询
User user1=mapper1.findUserById(1);
System.out.println(user1);
//在close的时候,才会将数据写入二级缓存中
sqlSession1.close();
//执行用户添加操作
mapper3.insertUser(user1);
//执行commit时,将一级缓存清空
sqlSession3.close();
//第二次查询
User user2=mapper2.findUserById(1);
//System.out.println(user2);
sqlSession2.close();
}
3、其他
(1)禁用缓存:
useCache=”false”
<select id="findUserById" useCache="false" parameterType="int" resultType="com.san.po.User">
SELECT
* FROM USER WHERE id =#{id}
</select>
(2)刷新缓存:
flushCache=”true”:刷新缓存,在select语句中,默认值是false,在增删改语句中,默认值是true
<select id="findUserById" flushCache="true" parameterType="int" resultType="com.san.po.User">
SELECT
* FROM USER WHERE id =#{id}
</select>
四、整合ehcache
1、ehcache概述
(1)Ehcache是一个分布式的缓存框架
(2)Mybatis本身是一个持久层框架,它不是专门的缓存框架,所以它对缓存的实现不够好,不能支持分布式。
2、分布式概述
(1)系统为了提高性能,通常会对系统采用分布式部署(集群部署方式)
(2)图解:
3、整合思路
(1)Cache是一个接口,它的默认实现是mybatis的PerpetualCache。如果想整合mybatis的二级缓存,那么实现Cache接口即可
(2)图解:
4、整合步骤
(1)添加jar包
(2)设置映射文件中cache标签的type值为ehcache的实现类
(3)添加ehcache的配置文件
在config下,创建ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<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、其他
(1)应用场景:
对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。
注意:在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需求来设置,比如设置30分钟、60分钟等,单位为毫秒。
(2)局限性:
Mybatis二级缓存对细粒度的数据,缓存实现不好。
场景:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都是最新的商品信息,此时如果使用二级缓存,就无法实现当一个商品发生变化只刷新该商品的缓存信息而不刷新其他商品缓存信息,因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。
解决此类问题,需要在业务层根据需要对数据有针对性的缓存。
比如可以对经常变化的 数据操作单独放到另一个namespace的mapper中