MyBatis-延迟加载与MyBatis缓存(面试题)

MyBatis-延迟加载与MyBatis缓存(面试题)MyBatis-延迟加载与MyBatis缓存-概念性MyBatis-延迟加载与MyBatis缓存MyBatis-延迟加载与MyBatis缓存-概念性延迟加载(面试题)1、什么是延迟加载(按需加载)2、延迟加载MyBatis缓存(面试题)1、Cache缓存2、MyBatis缓存分析3、一级缓存4、二级缓存原理开启二级缓存5、禁用二级缓存6、刷新二级缓存延迟加载(面试题)1、什么是延迟加载(按需…

大家好,又见面了,我是你们的朋友全栈君。

MyBatis-延迟加载与MyBatis缓存-概念性

延迟加载(面试题)

1、什么是延迟加载(按需加载

 resultMap中的association(has a)和collection(has some)标签具有延迟加载的功能。
 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。需要关联信息时再去按需加载关联信息。这样会大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

  • 设置延迟加载(配置问题使用代码演示)
    Mybatis默认是没开启延迟加载功能的,我们需要手动开启。
  • 需要在mybatis-config.xml文件中,在标签中开启延迟加载功能。
    lazyLoadingEnabled、aggressiveLazyLoading
    在这里插入图片描述
     在最新官方MyBatis文档里,有上面这2个属性,一个是延迟加载,一个是分层加载。
    lazyLoadingEnabled 默认值为false,那么在有级联关系的resultMap里,查询后会加载出所有的级联关系,当然有时候我们并不需要这些所有的时候,我们就可以应用到延迟加载给我们带来的好处了。
    aggressiveLazyLoading默认值是true,这里我称之为分层加载,大概意思是如果它为true,那么当我使用了延迟加载,要么所有级联都不加载,要么如果我加载一个,其他都得加载.
    aggressiveLazyLoading值是false 那么就是按需加载,如果是true表示只要使用一个级联对象,就全部加载!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 全部配置
    在这里插入图片描述
  • 局部配置
     fetchType是可以注明在association 和 collection里的,选值为eager和lazy,就是为了方便我们结合aggressiveLazyLoading(false)来配合使用的,让延迟加载发挥到极致,即只加载我需要的!
    在这里插入图片描述
    如果全局和局部同时生效,那么就近原则,局部生效!

2、延迟加载

在这里插入图片描述
以下是resultMap对应的POJO

  • Student:
public class Student { 
   

	private Integer id;
	private String studentName;
	private String studentAge;

	private List studentHealthCards;
	private ParentOfStudent parentOfStudent;

	public Integer getId() { 
   
		return id;
	}

	public void setId(Integer id) { 
   
		this.id = id;
	}

	public String getStudentName() { 
   
		return studentName;
	}

	public void setStudentName(String studentName) { 
   
		this.studentName = studentName;
	}

	public String getStudentAge() { 
   
		return studentAge;
	}

	public void setStudentAge(String studentAge) { 
   
		this.studentAge = studentAge;
	}

	public List getStudentHealthCards() { 
   
		return studentHealthCards;
	}

	public void setStudentHealthCards(List studentHealthCards) { 
   
		this.studentHealthCards = studentHealthCards;
	}

	public ParentOfStudent getParentOfStudent() { 
   
		return parentOfStudent;
	}

	public void setParentOfStudent(ParentOfStudent parentOfStudent) { 
   
		this.parentOfStudent = parentOfStudent;
	}

	@Override
	public String toString() { 
   
		return "Student [id=" + id + ", studentName=" + studentName + ", studentAge=" + studentAge + "]";
	}

}
  • StudentHealthCard:
public class StudentHealthCard { 
   

	private Integer id;
	private Integer stu_id;
	private String name;
	private String message;

	public Integer getId() { 
   
		return id;
	}

	public void setId(Integer id) { 
   
		this.id = id;
	}

	public Integer getStu_id() { 
   
		return stu_id;
	}

	public void setStu_id(Integer stu_id) { 
   
		this.stu_id = stu_id;
	}

	public String getName() { 
   
		return name;
	}

	public void setName(String name) { 
   
		this.name = name;
	}

	public String getMessage() { 
   
		return message;
	}

	public void setMessage(String message) { 
   
		this.message = message;
	}

	@Override
	public String toString() { 
   
		return "StudentHealthCard [id=" + id + ", stu_id=" + stu_id + ", name=" + name + ", message=" + message + "]";
	}
}
  • ParentOfStudent:
public class ParentOfStudent { 
   

	private Integer id;
	private Integer stu_id;
	private String name;

	public Integer getId() { 
   
		return id;
	}

	public void setId(Integer id) { 
   
		this.id = id;
	}

	public Integer getStu_id() { 
   
		return stu_id;
	}

	public void setStu_id(Integer stu_id) { 
   
		this.stu_id = stu_id;
	}

	public String getName() { 
   
		return name;
	}

	public void setName(String name) { 
   
		this.name = name;
	}

	@Override
	public String toString() { 
   
		return "ParentOfStudent [id=" + id + ", stu_id=" + stu_id + ", name=" + name + "]";
	}
}
  • Mapper文件
<?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">
<mapper namespace="com.mybatis.mapper.StudentMapper">

	<resultMap type="Student" id="stu">
		<id property="id" column="id" />
		<result property="studentName" column="studentName" />
		<result property="studentAge" column="studentAge" />
		<association property="parentOfStudent" column="id"
			select="selectOneParentOfStudent" fetchType="eager">
		</association>
		<collection property="studentHealthCards" column="id"
			ofType="Model.StudentHealthCard" 
			select="selectOneStudentHealthCard" fetchType="eager">
		</collection>
	</resultMap>
	
	<select id="selectOneStudent" resultMap="stu"> 
	    select * from student
		where id = #{ 
   id}		  
	</select>
	
	<select id="selectOneParentOfStudent" resultType="ParentOfStudent"> 
	    select * from
		parentofstudent where stu_id = #{ 
   id}		  
	</select>
	
	<select id="selectOneStudentHealthCard" resultType="StudentHealthCard"> 
	    select *
		from studenthealthcard where stu_id = #{ 
   id}		  
	</select>
	
</mapper>
  • 测试

  • 情况1:开启延迟加载,默认分层加载,不开启局部加载
    执行语句 Student student = sm.selectOneStudent(1);
    以下是运行结果:
    在这里插入图片描述
    执行语句:Student student = sm.selectOneStudent(1);
    student.getParentOfStudent();
    在这里插入图片描述
    这就是默认分层加载的后果,好的那么现在我把分层加载设置为false

  • 即情况2:开启延迟加载,分层加载false,不适用局部加载
    执行语句 Student student = sm.selectOneStudent(1);
    以下是运行结果:
    在这里插入图片描述
    执行语句:Student student = sm.selectOneStudent(1);
    student.getParentOfStudent();
    在这里插入图片描述
    好了 3条sql变成了2条

  • 情况3:就是使用fetchType的情况下,可以指明即使在延迟加载情况下也可以立即加载某个级联关系!

MyBatis缓存(面试题)

1、Cache缓存

在这里插入图片描述
缓存中有,先查询缓存。缓存中没有,那么查询数据库。这样的话不用每次都查询数据库。减轻数据库的压力。提高查询率!!!

第一次查询的时候,由于缓存中没有,那么去查询数据库返回给客户端。同时还会把这个次查询的数据放入缓存。
第二次查询同样的数据时候,发现缓存中曾经有查询过的数据,那么直接从缓存中读取。不必再次查询数据库,减轻数据库压力!

2、MyBatis缓存分析

 mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能。
在这里插入图片描述
 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

&emsp;Mybatis的缓存,包括一级缓存和二级缓存
 一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
一级缓存是session级别的,同一个session! 1级缓存是系统自带,不需要手动开启!

 二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是二级缓存区域。二级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。二级缓存中的value,就是查询出的结果对象。
二级缓存,可以跨session!二级缓存是要配置,然后手动开启!

一级缓存是默认使用的。
 二级缓存需要手动开启。

Map<String,Object> key 缓存标志 Value 缓存的数据

3、一级缓存

在这里插入图片描述
 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
Mybatis默认支持一级缓存。

  • 测试1
@Test
	public void test1(){ 
   
		Student s1 = mapper.selectOneStudent(1);
		Student s2 = mapper.selectOneStudent(1);
		System.out.println(s1==s2);
	}
  • 测试2
@Test
	public void test1(){ 
   
		Student s1 = mapper.selectOneStudent(1);
		
		//session.commit();
		//session.clearCache();
		
		Student s2 = mapper.selectOneStudent(1);
		System.out.println(s1==s2);
	}
  • 应用
    正式开发,是将mybatis和spring进行整合开发,事务控制在service中。
    一个service方法中包括很多mapper方法调用。
service{ 
   
        //开始执行时,开启事务,创建SqlSession对象

	    //第一次调用mapper的方法findUserById(1)
        //第二次调用mapper的方法findUserById(1),从一级缓存中取数据

       
        //方法结束,sqlSession关闭
}

第一次调用mapper的方法findUserById(1)
第二次调用mapper的方法findUserById(1),从一级缓存中取数据
如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

4、二级缓存

原理

下图是多个sqlSession请求UserMapper的二级缓存图解。
在这里插入图片描述
二级缓存是mapper级别的。
第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区域内。
第二次调用相同namespace下的mapper映射文件(xml)中相同的SQL去查询用户信息。会去对应的二级缓存内取结果。
如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存

开启二级缓存

Mybatis默认是没有开启二级缓存
 1.在核心配置文件myBatis-config.xml中加入以下内容(开启二级缓存总开关):
 在settings标签中添加以下内容:
在这里插入图片描述
&emsp2.在StudentMapper映射文件中,加入以下内容,开启二级缓存:
在这里插入图片描述
 3.实现序列化(持久化)
在这里插入图片描述
由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。
缓存默认是存入内存中,但是如果需要把缓存对象存入硬盘那么久需要序列化(实体类要实现)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果该类存在父类,那么父类也要实现序列化。
在这里插入图片描述测试1:

@Test
	public void test2(){ 
   
		SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
		SqlSession session1 = factory.openSession();
		StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
		Student s1 = mapper1.selectOneStudent(1);
		System.out.println(s1);
		session1.close();
		
		SqlSession session2 = factory.openSession();
		StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
		Student s2 = mapper2.selectOneStudent(1);
		System.out.println(s2);
	}

测试2:
@Test

public void test2(){ 
   
		SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
		
		SqlSession session1 = factory.openSession();
		StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
		Student s1 = mapper1.selectOneStudent(1);
		System.out.println(s1);
		session1.close();
		SqlSession session2 = factory.openSession();
		StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
		s1.setStudentName("王二小");
		mapper2.updateStudent(s1);
		session2.commit();
		session2.close();
		
		SqlSession session3= factory.openSession();
		StudentMapper mapper3 = session3.getMapper(StudentMapper.class);
		Student s2 = mapper3.selectOneStudent(1);
		System.out.println(s2);
	}

根据SQL分析,确实是清空了二级缓存了

5、禁用二级缓存

该statement中设置useCache=false,可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存。
在这里插入图片描述

6、刷新二级缓存

 该statement中设置flushCache=true可以刷新当前的二级缓存,默认情况下如果是select语句,那么flushCache是false。如果是insert、update、delete语句,那么flushCache是true。
 如果查询语句设置成true,那么每次查询都是去数据库查询,即意味着该查询的二级缓存失效。
 如果查询语句设置成false,即使用二级缓存,那么如果在数据库中修改了数据,而缓存数据还是原来的,这个时候就会出现脏读。
在这里插入图片描述
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/143645.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 免费国内php空间_全球vps交流网站超级vps管理器

    免费国内php空间_全球vps交流网站超级vps管理器网站名称:000webhost.com250MB硬盘空间,100GB数据流量有足够的空间存放你的网站,emails 和数据库. 服务器为百兆独享接入Internet, 所以可以提供100G的数据流量.PHP 和MySQL 数据库支持不想其他免费空间,对php和mysql的功能进行限制.在这里你可以使用最新版本的php和mysql. 所有以下php特性都支持:

  • MFC的UDP编程实现[通俗易懂]

    MFC的UDP编程实现[通俗易懂]1、编程原理UDP是面向非连接的通信协议,比TCP协议简单很多。无论是服务器端还是客户端,其通信过程概括为:创建套接字(socket)–>绑定(bind)–>发送send(或接收recv)–>关闭套接字(closesocket) 2、特殊地址:在实际通信网络中,我们几乎不会用到“0.0.0.0″和“127.0.0.1”这样的IP地址。但是在一台计算机上,特别用于某些测试用

  • C++学习——四种字符串与数据连接的方法

    C++学习——四种字符串与数据连接的方法方法调用很简单,sprintf itoa函数在我的其他博客也有详解,请翻阅查看,话不多说,直接撸代码:#include <iostream>#include <string>#include <cstdlib>#include <sstream>#include <cstring> using namespace std;…

  • java.lang.assertionerror_java parseint

    java.lang.assertionerror_java parseintMicrosoftVisualStudioSolutionFile,FormatVersion12.00#VisualStudio15VisualStudioVersion=15.0.26730.16MinimumVisualStudioVersion=10.0.40219.1Project(“{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC…

  • 打造自己的Android源码学习环境之一:序[通俗易懂]

    打造自己的Android源码学习环境之一:序[通俗易懂]打造自己的Android源码学习环境之一:序为什么要打造自己的Android源码学习环境有一个可以编译的Android源码环境,可以在任何自己想了解的源码中加上log信息,验证自己的理解是否准确,有助于理解Android的运行细节。做Android开发,了解Android的运行机制和原理是很有帮助的,尤其是想对Androidframework进行深度定制。面向的读者如果在手机厂商或者Android

  • pycharm关闭自动补全_python opencv 教程

    pycharm关闭自动补全_python opencv 教程我刚下载pycharm,准备学opencv,然后在网上博客上找了许多文章看了,有的说下载后导入修改cv2文件夹里的_init_.py,但是经过测试也不行,个人感觉总是少了啥,然后找了许多文章看了然后也试了,除了安装opencv成功之外就没有了。后来看了许多文章之后看见每个博客写的方法都不一样,这里我就有点顿悟了…我看到cv2文件夹中的_init_.py里面的code已经说清楚啦,我是刚开始不怎么看得懂英文的意思,后来仔细看了才大概明白了一丢丢…请看:importimportlibimport

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号