我们开发过程中效率最慢的就是数据库的操作部分,所以我们想尽办法去提高数据库的性能,今天介绍的延迟加载和缓存机制就是提升数据库访问性能的两种方式,但是我们在真实的开发环境中往往不会使用的。往往我们会使用Redis来提升性能,在Redis面前这个缓存机制就是小儿科。但是不使用并不代表不需要了解。
文章目录
- 前言
- 一、延迟加载?
- 二、缓存机制
- 1.一级缓存
- 2.二级缓存
- 总结
前言
一、延迟加载?
当查询一个对象时,与它相关联的对象的查询尽量的往后延,延到我要使用这个相关联的对象的属性时,才进行查询。要知道数据资源的访问是比较消耗资源的。而不需要的关联的对象我们就没必要再去查询。
举例:
我们知道在查询一个员工的时候有时候需要查询该员工对应的部门信息,但是有时候是不需要查询部门信息的,我们只是想知道该员工的一些信息,如果不使用延时加载的话就会造成资源的浪费。看案例:
<select id="getEmployeeByid" resultMap="huhuuhu" >
select e.*from employee e where e_id=#{id}
</select>
<resultMap id="huhuuhu" type="Employee">
<id column="e_id" property="id"></id>
<result column="e_name" property="name"></result>
<result column="e_gender" property="gender"></result>
<result column="e_age" property="age"></result>
<association property="department" javaType="Department"
column="e_depart_id" select="com.offcn.mapper.DepartMapper.getDepartmentByIdTW"
>
</association>
</resultMap>
测试类:
@Test
public void show15() {
SqlSession session = MyBatisUtils.getSession();
EmployMapper mapper = session.getMapper(EmployMapper.class);
Employee employeeByid = mapper.getEmployeeByid(15);
System.out.println(employeeByid.getName());
MyBatisUtils.closeSession(session);
}
我们看到测试类中只查询了员工信息而并没有查询部门信息,看一下生成的sql
使用延时加载机制。
在association标签或者collection标签中添加属性fetchType=“lazy”同一样的测试类
这时我们可以看出,在没有使用到部门信息时,只是进行了一次查询
改变测试类我们再看看:
//此时我们的输出语句就用到了部门信息
System.out.println(employeeByid.getName()+"所在的部门为:"+employeeByid.getDepartment().getName());
结果如下:
问题有没有一种方法可以设置整个项目中采用延迟加载呢?
既然问了那就当然有!
在MyBatis的核心配置文件中配置
<settings>
<!--开启全局延加载开关-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
如果想局部的想使用立即加载的方式的话,则在association或collection的标签中设置属性fetchType=“eager”
fetchType=“eager” 表示立即加载
fetchType=“lazy” 表示延迟加载
二、缓存机制
什么是缓存?缓存就是存储数据的一个地方我们称之为(Cache)当我们查询数据的时候如果缓存中有我们需要的数据时,就不需要去数据库中进行查询,而是直接使用缓存中的数据。我们知道查询数据库是非常的耗时的,数据库的数据是存储在硬盘上的,而硬盘的读取效率又是最低的,而缓存是存放在内存中的,要知道内存中读取数据是非常快的。这样就可以用来提升性能。缓存一般存储那些频繁查询且经常不变的数据。
1.一级缓存
一级缓存是SqlSession级别的缓存,MyBatis默认就是开启的。我们在使用同一个SqlSession对象调用一个Mapper方法时,如果参数和方法是一模一样的,那么我们MyBatis只用执行一次sql语句即可,因为在第一次查询后MyBatis会将其放进缓存中,当再次查询时直接取出数据即可(没有刷新,缓存没有超时前提)
代码演示:
@Test
public void show15() {
SqlSession session = MyBatisUtils.getSession();
EmployMapper mapper = session.getMapper(EmployMapper.class);
Employee employee1 = mapper.getEmployeeByid(15);
System.out.println("第一次查询员工编号为15的员工为:"+employee1.getName());
Employee employee2 = mapper.getEmployeeByid(15);
System.out.println("第二次查询员工编号为15的员工为:"+employee2.getName());
MyBatisUtils.closeSession(session);
}
看一下执行过程:
上述可以看出代码执行了两次,但是数据的操作仅执行了一次。这里就用到了一级缓存。一级缓存原理:
一级缓存的清除:
1:在查询前进行DML操作(delete/update/insert),此时会清空一级缓存这样的目的也是为了使缓存中的数据保持最新。避免脏读
2:使用SqlSession中的方法clearCache()
3:在Mapper.xml文件的select标签中添加属性flushCache = true
清除缓存后的查询过程:
2.二级缓存
我们看到一级缓存是需要在同一个SqlSession中,第二次查询时才会有效率上的提升。那么除了sqlsession中哪里还能存放缓存呢?
二级缓存是基于SqlSessionFactory的。它的底层实现原理简单说也是一个Map,类似于这样
Map<String,Map<String,Object>> map;
第一个 String 是当前的命令空间的全称。“cn.offcn.mapper.EmployeeMapper”
第二个 String 是查询条件
二级缓存的实现:
1.核心配置文件
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
因为cacheEnabled默认就是为true,因此该配置可以不写。
2.配置映射文件:
<mapper namespace="com.offcn.mapper.UserMapper">
<!--该映射文件开启二级缓存机制-->
<cache></cache>
<select useCache="true"><!--该statement使用二级缓存-->
</select>
</mapper>
3.对应的实体类要实现序列化接口
4.测试类:
@Test
public void test(){
SqlSession session = MyBatisUtils.getSession();
EmployMapper mapper = session.getMapper(EmployMapper.class);
Employee employeeCache = mapper.getEmployeeCache(13);
System.out.println(employeeCache.getName());
session.commit();
SqlSession session2 = MyBatisUtils.getSession();
EmployMapper mapper2 = session2.getMapper(EmployMapper.class);
Employee employeeCache2 = mapper2.getEmployeeCache(13);
System.out.println(employeeCache2.getName());
MyBatisUtils.closeSession(session);
MyBatisUtils.closeSession(session2);
}
结果: ![在这里插入图片描述]()
总结
1.一二级缓存都存在的情况下,会先访问二级缓存,然后再访问一级缓存,最后才会访问数据库
2.select元素的flushCache属性置为true,最终会清除一级缓存所有数据,同时会清除这个select所在的namespace对应的二级缓存中所有的数据
3.select元素的useCache置为false,会使这个查询跳过二级缓存