一、延迟加载

1.主对象的加载:

根本没有延迟的概念,都是直接加载。

2.关联对象的加载时机:

01.直接加载:

访问主对象,关联对象也要加载

02.侵入式延迟:

访问主对象,并不加载关联对象

访问主对象属性的属性的时候,关联对象会被加载

03.深度延迟

访问主对象,并不加载关联对象

访问主对象的属性的时候,关联对象也不会被加载

访问关联对象或关联对象的属性的时候,才会加载关联对象。

3.一对多延迟加载代码:

01.实体类代码:

package cn.pb.bean;

import java.util.Set;

/**
* 国家的实体类
*/
public class Country {
private Integer cId;//国家的编号
private String cName;//国家的名称 //关联省会的属性
private Set<Provincial> provincials; public Integer getcId() {
return cId;
} public void setcId(Integer cId) {
this.cId = cId;
} public String getcName() {
return cName;
} public void setcName(String cName) {
this.cName = cName;
} public Set<Provincial> getProvincials() {
return provincials;
} public void setProvincials(Set<Provincial> provincials) {
this.provincials = provincials;
} }
package cn.pb.bean;

/**
* 省会对应的实体类
*/
public class Provincial {
private Integer pId; //省会的编号
private String pName; //省会名称 public Integer getpId() {
return pId;
}
public void setpId(Integer pId) {
this.pId = pId;
}
public String getpName() {
return pName;
}
public void setpName(String pName) {
this.pName = pName;
}
}

02.dao层代码:

public interface CountryDao {
/**
* 根据国家id 查询国家的信息 以及国家下面的省会
*/
Country selectCountryById(Integer id);
}

03.mapper.xml代码:

<mapper namespace="cn.pb.dao.CountryDao">
  <!--01.先根据id查询出国家信息 多条sql的查询 可以使用延迟加载策略-->
<select id="selectCountryById" resultMap="countryMap">
select cid,cname from country where cid=#{xxx}
</select>

  <!--03.根据国家id 查询出省份信息 -->
    <select id="selectProvincialByCountryId" resultType="Provincial">
select pid,pname from provincial
where countryid=#{xxx}
<!--#{xxx} 对应的就是resultMap中 collection节点下面的column -->
</select>
  <!--02.国家的映射信息-->
    <resultMap id="countryMap" type="Country">
<id property="cId" column="cid"/>
<result property="cName" column="cname"/>
<!--设置关联的集合属性
select:需要关联的查询语句
column: select关联语句中需要的参数 -->
<collection property="provincials" ofType="Provincials"
select="selectProvincialByCountryId"
column="cid"/>
</resultMap> </mapper>

04.测试代码:

public class CountryTest {
CountryDao dao=null;
SqlSession session=null;
Logger log= Logger.getLogger(CountryTest.class); @Before
public void before(){ //获取session
session= SessionFactoryUtil.getSession();
//获取执行的类对象
dao=session.getMapper(CountryDao.class);
} /**
* 在所有的test测试方法执行之后 都要执行的操作
*/
@After
public void after(){
if(session!=null){
session.close();
}
}   <!--只输出主加载对象 只会有一条sql语句-->
@Test
public void testSelectCountryById(){
Country country=dao.selectCountryById(1);
log.debug("根据id查询国家信息"+country);
}   
  <!--输出关联对象的信息 会有两条sql语句-->
@Test
public void testSelectCountryById(){
Country country=dao.selectCountryById(1);
log.debug("根据id查询国家信息"+country.getProvincials());
    }
}

4.配置延迟加载:

在mybatis.xml文件中配置:

<settings>
<!-- 全局性地启用或禁用延迟加载。当禁用时,所有关联的配置都会立即加载。 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当启用后,一个有延迟加载属性的对象的任何一个延迟属性被加载时,该对象
的所有的属性都会被加载。否则,所有属性都是按需加载。 侵入式延迟 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

二、MyBatis缓存

1.查询缓存的作用:

查询缓存的使用,主要是为了提供查询访问速度。将用户对同一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。

2.关于缓存的说明:

01.MyBatis查询缓存机制。根据缓存区的作用域与生命周期,可划分为两种:一级缓存和二级缓存。

02.MyBatis查询缓存的作用域是根据映射文件的namespace划分的,相同的namespace的mapper查询数据放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。

03.但是一级、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期与整个应用同步,与SqlSession是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共享数据,而二级缓存是在不同线程(不同的SqlSession)间共享数据。

04. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其生命周期为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。

05. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

06. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

3.一级缓存:

01.一级缓存存在的证明代码:

001.dao层代码:

/**
* 根据学生的编号查询对应的信息
* 验证一级缓存的存在
*/
Student selectStudentById(Integer sId);

002.mapper.xml代码:

<mapper namespace="cn.pb.dao.StudentDao">
<!-- 查询指定学生的信息 验证一级缓存的存在 -->
<select id="selectStudentById" resultType="Student">
select sid,sname from stu where sid=#{xxx}
</select>
</mapper>

003.测试代码:

package cn.pb;

import cn.pb.bean.Student;
import cn.pb.dao.StudentDao;
import cn.pb.util.SessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class StudentTest {
StudentDao dao;
SqlSession session;
Logger logger=Logger.getLogger(StudentDao.class);
@Before
public void before(){
session= SessionFactoryUtil.getSession();
dao=session.getMapper(StudentDao.class);
} /**
* 在所有的test测试方法执行之后 都要执行的操作
*/
@After
public void after(){
if(session!=null){
session.close();
}
} /**
* 验证一级缓存的存在
* myBatis的一级缓存是一直开启的,并且不能关闭!
*/
@Test
public void test1(){
Student student=dao.selectStudentById(1);
logger.debug("第一次查到的id为1的学生:"+student);
//再次查询相同的id对象
Student student2 = dao.selectStudentById(1);
logger.debug("第2次查到的id为1的学生:"+student2);
}
}

02.一级缓存--从缓存中查找数据的依据:

001.缓存的底层实现是一个Map,Map的value是查询结果

002.Map的key,即查询依据,使用的ORM架构不同,查询依据是不同的

003.MyBatis的查询依据是:Sql的id+SQL语句

004.Hibernate的查询依据是:查询结果对象的id

005.验证代码:

a.dao层代码:

/**
* 验证mybatis缓存查询的依据!
*/
Student selectStudentById2(Integer sId);

b.mapper.xml代码:

<mapper namespace="cn.pb.dao.StudentDao">

    <!-- 查询指定学生的信息    验证mybatis缓存查询的依据!
两个查询语句的id不一致,但是sql语句一样-->
<select id="selectStudentById2" resultType="Student">
select sid,sname from stu where sid=#{xxx}
</select> </mapper>

c.测试代码:

/**
* 验证查询的依据
* 两个查询都是查询id为1的学生对象,但是查询语句的id不一致
*/ @Test
public void test2() {
Student student = dao.selectStudentById(1);
logger.debug("第1次查到的id为1的学生:"+student);
//再次查询相同的id对象
Student student2 = dao.selectStudentById2(1);
logger.debug("第2次查到的id为1的学生:"+student2);
}
  得到的结论是:
mybatis的查询依据是 : mapper文件中sql的id + sql语句!
hibernate底层查询的依据是: 查询对象的id! 其实缓存的底层是一个map,
map的key就是查询依据,value是查询的结果!

03.增、删、改对一级缓存的影响:

增删改会清空一级缓存:注意:必须使用insert标签,不能使用select,否则实验做不成功

001.dao层代码:

/**
* 验证增删改查对一级缓存的影响!
*/
void addStudent(Student student);

002.mapper.xml代码:

<mapper namespace="cn.pb.dao.StudentDao">
<!-- 查询指定学生的信息 验证一级缓存的存在 -->
<select id="selectStudentById" resultType="Student">
select sid,sname from stu where sid=#{xxx}
</select> <!--新增一个学生-->
<insert id="addStudent">
<!--#{sId},#{sName} 对应的是实体类中的属性名 -->
insert into stu values(#{sId},#{sName})
</insert>
</mapper>

003.测试代码:

package cn.pb;

import cn.pb.bean.Student;
import cn.pb.dao.StudentDao;
import cn.pb.util.SessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class StudentTest {
StudentDao dao;
SqlSession session;
Logger logger=Logger.getLogger(StudentDao.class);
@Before
public void before(){
session= SessionFactoryUtil.getSession();
dao=session.getMapper(StudentDao.class);
} /**
* 在所有的test测试方法执行之后 都要执行的操作
*/
@After
public void after(){
if(session!=null){
session.close();
}
} /**
* 验证增删改对一级缓存的影响
* 之前是只有一条查询语句!
* 但是加上新增语句之后发现出现了再次查询!
*
* 因为增删改查操作都要清空缓存,把数据同步到数据库,
* 保证后续的查询得到正确的结果集!
*/
@Test
public void test3() { Student student = dao.selectStudentById(1);
logger.debug("新增之前查询到的stuent:"+student);
dao.addStudent(new Student(55, "新增学生"));
session.commit();
//再次查询相同的id对象
Student student2 = dao.selectStudentById(1);
logger.debug("新增之后查询到的student:"+student2);
} }

4.二级缓存:

01.内置二级缓存

001.由于MyBatis从缓存中读取数据的依据与SQL的id相关,而非查询出的对象。所以,使用二级缓存的目的,不是在多个查询间共享查询结果(所有查询中只要查询结果中存在该对象,就直接从缓存中读取,这是对查询结果的共享,Hibernate中的缓存就是为了在多个查询间共享查询结果,但MyBatis不是),而是为了防止同一查询(相同的Sql id,相同的sql语句)的反复执行。

002.MyBatis内置的二级缓存为

https://my.oschina.net/KingPan/blog/280167

http://www.mamicode.com/info-detail-890951.html

http://blog.csdn.net/isea533/article/details/44566257

http://blog.csdn.net/u010676959/article/details/43953087

http://blog.csdn.net/xiadi934/article/details/50786293

02.如何开启二级缓存---三条件

001.你cacheEnabled=true,默认值为true

002.你得在Mapper文件中,<cache/>

003.Entity Implements Serializable

03.增删改对二级缓存的影响

001.增删改也会清空二级缓存

002.对于二级缓存的清空实质上是对value清空为null,key依然存在,并非将Entry<k,v>删除

003.从DB中进行select查询的条件是:

  0001.缓存中根本不存在这个key

  0002.存在key对应的Entry,但是value为null

04.二级缓存的配置

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用, 而且返回的对象被认为是只读的

05.可用的收回策略

001.LRU – 最近最少使用的:移除最长时间不被使用的对象。

002 FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

003.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

004.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

默认的是 LRU。

005.flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒

006.形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

007.size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。

008.readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回 缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。 可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。

06.二级缓存的关闭:

   001.局部关闭

  在mapper文件中修改

    <select id="selectStudentById"  useCache="false" resultType="Student">
  增加了useCache="false" 相当于 局部关闭 2级缓存 useCache默认值为true===》把查询放入2级缓存

  002.全局关闭

  在mybatis.xml文件中增加

      <settings>
    <!--全局关闭2级缓存 -->
  <setting name="cacheEnabled" value="false"/>
</settings>

 

07.二级缓存的使用原则:

001. 很少被修改的数据
002. 不是很重要的数据,允许出现偶尔并发的数据
003. 不会被并发访问的数据
004.多个namespace不能操作同一张表
005.不能在关联关系表上执行增删改操作

08.代码验证mybatis2缓存

001. dao层代码:
  
public interface StudentDao {
/**
* 验证mybatis2级缓存!
*/
Student selectStudentById(Integer sId);
/**
* 验证增删改查对2级缓存的影响!
*/
void addStudent(Student student);
}
002. mapper.xml代码:
<mapper namespace="cn.pb.dao.StudentDao">
<!--配置二级缓存-->
<cache/>
<select id="selectStudentById" resultType="Student">
select sid,sname from stu where sid=#{xxx}
</select>
<!-- 新增一个学生 验证对2级缓存的影响 标签中 增加 flushCache="false" 可以设置在新增数据的时候不刷新2级缓存
但是一级缓存不能配置 也就是 只要是一级缓存的增删改 都会刷新 -->
<insert id="addStudent">
<!--#{sId},#{sName} 对应的是实体类中的属性 -->
insert into stu values (#{sId},#{sName})
</insert> </mapper>

 

003. 测试代码:
package cn.pb;

import cn.pb.bean.Student;
import cn.pb.dao.StudentDao;
import cn.pb.util.SessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class TestStudent {
StudentDao dao;
SqlSession session;
Logger logger=Logger.getLogger(TestStudent.class); @Before
public void before(){
session= SessionFactoryUtil.getSession();
dao=session.getMapper(StudentDao.class);
} /**
* 在所有的test测试方法执行之后 都要执行的操作
*/
@After
public void after(){
if(session!=null){
session.close();
}
} /**
* 验证2级缓存
*
* 开启内置2级缓存的步骤
* 01.实体类对象 要实现serializable 序列化接口
* 02.在mapper文件中 增加 <cache/>节点
*/
@Test
public void test1() {
Student student = dao.selectStudentById(1);
System.out.println(student); session.close(); //关闭了session 一级缓存中的数据肯定清空了 session = SessionFactoryUtil.getSession(); //再次获取session 查询数据
dao = session.getMapper(StudentDao.class);
//这时候不会再有sql语句了 因为2级缓存中存在相同的查询(mapper文件中sql的id)和相同的sql语句
Student student2 = dao.selectStudentById(1);
System.out.println(student2);
} /**
* 验证增删改对2级缓存的影响
*/
@Test
public void test2() {
Student student = dao.selectStudentById(1);
System.out.println(student); session.close(); //关闭了session 一级缓存中的数据肯定清空了 session = SessionFactoryUtil.getSession(); //再次获取session 查询数据
dao = session.getMapper(StudentDao.class);
//新增学生信息 看看对2级缓存的影响
dao.addStudent(new Student(22,"测试")); Student student2 = dao.selectStudentById(1);
System.out.println(student2);
} }

MyBatis延迟加载和缓存的更多相关文章

  1. MyBatis延迟加载和缓存(4)

    一.项目创建 1.项目目录结构 2.数据库配置和上一篇的一样,这里不再描述.下面创建mybatis配置文件SqlMapConfig.xml <?xml version="1.0&quo ...

  2. MyBatis延迟加载及缓存

    延迟加载 lazyLoadingEnabled 定义: MyBatis中的延迟加载也成为懒加载,就是在进行关联查询的时候按照设置延迟加载规则推迟对关联对象的select检索.延迟加载可以有效的减少数据 ...

  3. Mybatis延迟加载、缓存

    一.Mybatis中的延迟加载 1.延迟加载背景:Mybatis中Mapper配置文件中的resultMap可以实现高级映射(使用association.collection实现一对一及一对多(多对多 ...

  4. (二)MyBatis延迟加载,一级缓存,二级缓存

    延迟加载配置: 什么时候用延迟加载?比如现在有班级和学生表,一对多关系,你可能只需要班级的信息,而不需要该班级学生的信息,这时候可以进行配置,让查询时先查询到班级的信息,在之后需要学生信息时候,再进行 ...

  5. MyBatis 延迟加载,一级缓存,二级缓存设置

    什么是延迟加载 resultMap中的association和collection标签具有延迟加载的功能. 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再去加载关联信息 ...

  6. Mybatis延迟加载和查询缓存

    摘录自:http://www.linuxidc.com/Linux/2016-07/133593.htm 阅读目录 一.延迟加载 二.查询缓存 一.延迟加载 resultMap可以实现高级映射(使用a ...

  7. mybatis多参数传递,延迟加载,缓存,注解开发

    1.Mybatis的多参数传递方式 需求:更具id 和 名字查询用户: select * from user where id = ? and name = ?: 1):QueryVo 或者 User ...

  8. Mybatis学习(五)————— 延迟加载和缓存机制(一级二级缓存)

    一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的数据的话,就不查询从表的信息.所以这就是突出了懒这个特点.真是懒啊. Mybati ...

  9. Mybatis(五) 延迟加载和缓存机制(一级二级缓存)

    踏踏实实踏踏实实,开开心心,开心是一天不开心也是一天,路漫漫其修远兮. --WH 一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的 ...

随机推荐

  1. display 的 32 种写法

    从大的分类来讲, display的 32种写法可以分为 6个大类,再加上 1个全局类,一共是 7大类: 外部值 内部值 列表值 属性值 显示值 混合值 全局值 外部值 所谓外部值,就是说这些值只会直接 ...

  2. 对网站视频资源的管控-禁止通过视频的url访问视频

    一般静态文件的下载是不经过PHP的,直接由web服务器发送到客户端.但有时候需要实现文件下载的权限控制等功能,这时候就需要经由PHP程序来做权限验证.简单粗暴的做法是,在PHP程序里边先验证权限,验证 ...

  3. Yii2数据库操作再总结

    User::find()->all(); 此方法返回所有数据:User::findOne($id); 此方法返回 主键 id=1 的一条数据(举个例子): User::find()->wh ...

  4. pep 8 规范的一些记录

    一.pep8起源 龟叔创立Python的初衷里就有创立一个容易阅读的编程语言,所以亲自操刀写了pep8 代码规范,每个项目开始前都要有一个共识,就是自己的代码规范,pep8 就是一个很好的范本. 二. ...

  5. HDU - 4545 字符串处理

    思路:对于每个字符,如果它能被替换一定要优先替换,其次再进行删除.遵循这个策略即可. 证明: 对于这题的第一个测试数据: abba addba 1 d b 当匹配到'b'  和 'd'时应该优先替换而 ...

  6. JVM笔记5-对象的访问定位。

    java虚拟机中指定一个栈内存的引用指向了堆内存中的对象.这样说只是笼统的说法.而指向堆内存中的对象就一定是栈引用所需要的那个对象吗?其实并不定. 这就需要知道对象的访问定位方式有两种: 1.使用句柄 ...

  7. H3C路由交换常用命令

    1.查看Linux下查看端口状态 root@root:~# netstat -an|grep -E "6002|6003" 2.H3C交换机显示当前配置 [H3C]display ...

  8. Nodejs的运行原理-libuv篇

    前言 这应该是Nodejs的运行原理的第7篇分享,这篇过后,短时间内不会再分享Nodejs的运行原理,会停更一段时间,PS:不是不更,而是会开挖新的坑,最近有在研究RPG Maker MV,区块链,云 ...

  9. ffmpeg结构体以及函数介绍(二)

    1 avcodec_find_decoder() /** * Find a registered decoder with a matching codec ID. * * @param id Cod ...

  10. Java StringBuilder 和 StringBuffer 源码分析

    简介 StringBuilder与StringBuffer是两个常用的操作字符串的类.大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的.前者是JDK1.5加 ...