1.什么是延迟加载?
延迟加载就是再需要用到数据时,才进行加载,不需要用到的数据时就不加载数据。延迟加载也称懒加载。
好处:
先从单表查询,需要时再从关联表去关联查询,大大提高了数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量得的数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验感下降。
所谓的懒加载就是把sql查询语句分成了两步 用select去连接另一个表的查询语句(一对一), 如果要查stuent表的内容就要通过懒加载 这里column定义了共有属性(变量) studentid
column在association 和 collection标签中都十分重要
第一步:开启懒加载
这里将回忆mybatis一对一,一对多,多对多中出现的resultMap集合所遗漏的知识点
首先讲:一对一懒加载[ “id”":为resultMap集合的地址, “type”" 为对应的实体类 (类型)(大小写无所谓), “property” 为实体类中对应的变量(对象)]
["column"为sql表中对应的属性名]
["association"为一对一的标签, “property” 为实体类中对应的变量(对象),列:另一个实体类的对象private Student student;, "javaType"为该对象的参数类型(类型)(大小写无所谓)
,“select"调用查询该对象的 “全限定名””+“方法名”]
[assocaistion中的"column" 指的是一对一两个sql表中用来关联的属性名(一般指id),例如:studentid]
[fetchType:懒加载两个值(lazy或者eager)]
<resultMap id="" type = "">
<id property="" column=""></id>
<result property = "" column = ""></reslut>
.
.
.
<association property="" javaType="" select"" column="" fetchType = ""
</association>
</resultMaop>
这里讲一对多的懒加载中用到的collection 标签
[因为collection标签后的property为一个集合对象,所以不需要javaType]
<resultMap id="" type = "">
<id property="" column=""></id>
<result property = "" column = ""></reslut>
.
.
.
<collection property="" column="" select="" fetchType = ""
</collection>
</resultMaop>
普通的collection标签 和 association标签
<resultMap id="findAllGoodsWithcategoryMap" type="Goods">
<association property="category" column="categoryid" javaType="Category">
</association>
</resultMap>
<resultMap id="categoryWithGoodsMap" type="category">
<collection property="goodsList" ofType="goods">
</collection>
</resultMap>
代码演示
在mybatisconfig.xml 配置里 vlue有 true 和 false 两种属性
<settings>
<!-- 开启全局懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
注意事项:
懒加载分为两种: | |
---|---|
第一种:一对一association延迟加载 | 第二种一对多collection延迟加载 |
select:为我们调用其他映射的id
column:为传递的参数
javaType:为查询到的java 数据类型
fetchType:是否使用懒加载,如果不设置与全局设置保持一致
一对一懒加载演示
ScoreDao
List<Score> findAllScoreWithStudent();
IStudentDao
IStudentDao:Student findStudentById(int id);
ScoreDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
xml;映射文件
-->
<mapper namespace="com.aaa.dao.ScoreDao">
<!--开启二级缓存-->
<cache >
<property name="eviction" value="LRU" />
<property name="flushInterval" value="600" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>
<select id="findScoreByStudentId" parameterType="Score" resultType="score">
select * from score where studentid = #{studentid}
</select>
<!-- 定义封装 -->
<resultMap id="findAllScoreWithStudentLazyMap" type="score">
<id property="scoreid" column="scoreid"></id>
<result property="score" column="score"></result>
<result property="coursename" column="coursename"></result>
<result property="studentid" column="studentid"></result>
<!-- <result property="studentid" column="studentid"></result> 这一行是否都不写? 还是在Lazy中不写?-->
<!-- 所谓的懒加载就是把sql查询语句分成了两步 用select去连接另一个表的查询语句(一对一)-->
<association property="student" javaType="student" select="com.aaa.dao.IStudentDao.findStudentById" column="studentid"
fetchType="lazy" ></association>
</resultMap>
<!-- scoreid coursename studentid score -->
<!-- 如果要查stuent表的内容就要通过懒加载 这里column定义了共有属性(变量) studentid -->
<select id="findAllScoreWithStudent" resultMap="findAllScoreWithStudentLazyMap">
select * from Score
</select>
</mapper>
测试
public class MyBatisLazyTest {
private SqlSession sqlSession;
@Before //在test 注解之前 调用
public void init() {
InputStream inputStream=null;
try {
inputStream = Resources.getResourceAsStream("mybatisconfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
sqlSession = sqlSessionFactory.openSession(true);
}
/*
*@Author lee
*@time:2020/7/11 10:41
*注释:一对一
*/
@Test
public void findAllScoreWithStudentLazyTest(){
/*加载实现类*/
ScoreDao scoreDao = sqlSession.getMapper(ScoreDao.class);
List<Score> scoreList = scoreDao.findAllScoreWithStudent();
for (Score score:scoreList){
/*System.out.println("Score:"+score.getCoursename());*/ /*有懒加载*/
/*System.out.println("Student:"+score.getStudentid());*/
System.out.println("Student:"+score.getStudent());
}
}
一对多
IStudentDao
List<Student> findAllStudentWithScore();
ScoreDao
Score findScoreByStudentId(int studentid);
IStudentDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
xml;映射文件
-->
<mapper namespace="com.aaa.dao.IStudentDao">
<!--开启二级缓存-->
<cache >
<property name="eviction" value="LRU" />
<property name="flushInterval" value="600" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>
<!-- parameter是参数类型 resultType是返回值类型 -->
<select id="findStudentById" resultType="student">
select * from student where id = #{id}
</select>
<resultMap id="findAllStudentWithRoleLazyMap" type="student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="sex" column="sex"></result>
<result property="age" column="age"></result>
<result property="height" column="height"></result>
<!-- 所谓的懒加载就是把sql查询语句分成了两步 用select去连接另一个表的查询语句(一对多)-->
<collection property="scoreList" column="id" select="com.aaa.dao.ScoreDao.findScoreByStudentId" fetchType="lazy">
</collection>
</resultMap>
<!-- id name sex age height listscore-->
<!-- 如果要查 listscore 就要通过懒加载,但是个一对多在实体中加的list集合所以没有column这个共有属性(变量)-->
<select id="findAllStudentWithScore" resultMap="findAllStudentWithRoleLazyMap">
select * from student
</select>
</mapper>
MyBatisLazyTest
public class MyBatisLazyTest {
private SqlSession sqlSession;
@Before //在test 注解之前 调用
public void init() {
InputStream inputStream=null;
try {
inputStream = Resources.getResourceAsStream("mybatisconfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
sqlSession = sqlSessionFactory.openSession(true);
}
/*
*@Author lee
*@time:2020/7/11 10:48
*注释:多对多懒加载
*/
@Test
public void findAllStudentWithScoreLazyTest(){
/*加载实现类*/
IStudentDao iStudentDao = sqlSession.getMapper(IStudentDao.class);
List<Student> studentList = iStudentDao.findAllStudentWithScore();
for (Student student :studentList){
System.out.println("student:"+student);
System.out.println("student:"+student.getId());
System.out.println("ScoreList:"+student.getScoreList());
}
}
}
2.MyBatis的缓存
缓存的作用|缓存:1.提高加载速度 2.减轻服务(web,mysql)压力
一级二级缓存调用顺序:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存;一个namespace中的查询更是如此;
使用二级缓存的实体类必须实现序列化;方便存储
级缓存;一个namespace中的查询更是如此;
使用二级缓存的实体类必须实现序列化;方便存储
缓存是一般的ORM框架都会提供的功能,目的就是提升查询的肖略和减少数据库的压力。跟Hibernate一样,MyBatis也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。
MyBatis的缓存分为两种 | |
---|---|
一级缓存基于sqlSession | 二级缓存基于namespace |
缓存的作用 | 缓存:1.提高加载速度 2.减轻服务(web,mysql)压力 |
关于脏数据 | 因为当一级二级缓存都开启的时候,会发生数据碰撞,但是当运行二级缓存的时候,关闭一级缓存就不产生脏数据 |
– | – |
一级二级缓存用的测试是不同的 | 因为要测试多个Sqlsessioon,进行数据的查询修改,看数据会不会发生改变 |
开启二级缓存,触发二级缓存需要sqlSession.commit()提交或者sqlSession.close()关闭
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
private SqlSessionFactory sqlSessionFactory;
@Before //在Test 注解调用之前调用
public void init(){
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatisconfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
/*SqlSessionFactoryBuilder 是建造者模式*/
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
/* 创建 mybatis 连接 mysql 的工厂 相当于 创建 jdbc中的connect工厂*/
sqlSessionFactory = builder.build(inputStream);
}
@Test
public void secondCacheTest(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
IStudentDao iStudentDao1 = sqlSession1.getMapper(IStudentDao.class);
Student student1 = iStudentDao1.findStudentById(1);
System.out.println("student1:"+student1);
// 提交 触发 二级缓存生效
// 发生更新,二级缓存失效
student1.setAge(77);
iStudentDao1.updateStudent(student1);
sqlSession1.commit();
IStudentDao iStudentDao2 = sqlSession2.getMapper(IStudentDao.class);
Student student2 = iStudentDao2.findStudentById(1);
System.out.println("student2:"+student2);
}
关闭一级缓存(关闭一级缓存的作用,避免脏数据的产生)
<!--关闭一级缓存-->
<setting name="localCacheScope" value="STATEMENT"/>
解决一级缓存时,产生的问题toString();
<!--解决 懒加载时: 打印对象toString 触发 懒加载
lazyLoadTriggerMethods : 指定那个对象的方法触发一次延迟加载,默认值:equal
System.out.println("student:"+student) 因为直接便利对象的时候会触发懒加载,所以引入"lazyLoadTriggerMethods"解决-->
<setting name="lazyLoadTriggerMethods" value="false"/>
二级缓存经常忘记的事情(需要手动调整flushInterval时间,进行缓存刷新,保证数据的新鲜性)
<mapper namespace="com.wgz.dao.IStudentDao">
<!--开启二级缓存-->
<cache >
<property name="eviction" value="LRU" />
<property name="flushInterval" value="6000000" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>
</mapper>
使用缓存:useCahce默认属性为true
<!--使用resultMap-->
<select id="findStudentById" parameterType="java.lang.Integer" resultType="student"
useCache="true">
select * from student_tb where id=#{id}
</select>
一级缓存是SqlSession
二级缓存是mapper(整个namespace命名空间)