精华内容
下载资源
问答
  • 一,MyBatis一级缓存(本地缓存)My Batis 一级缓存存在于 SqlSession 的生命周期中,是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构用来存储缓存数据。不同的SqlSession之.....

    写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下!

    一,MyBatis一级缓存(本地缓存)

    My Batis 一级缓存存在于 SqlSession 的生命周期中,是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构用来存储缓存数据。不同的SqlSession之间的数据缓存是不能共享的。

    在同一个SqlSession 中查询数据时,sqlSession会先在一级缓存中查找,如果有,直接读取,如果没有,则从数据库中查询, 接着把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一级缓存中(以Map对象的形式)。如果后面再次执行相同方法,SqlSession通过算法会生成相同的键值,然后在一级缓存中查找,由于一级缓存中己经存在该键值,所以会返回缓存中的对象。与执行select不同的是,执行update,insert,delect操作后会清空一级缓存中的数据,而不是通过算法生成缓存的键值存入一级缓存,之所以有这种差别是因为 select的flushCache(清空缓存)默认为false,而update,insert,delect的flushCache(清空缓存)默认为true。

    当然也可以使用下面的方法对select操作进行设置,

    select * from student where sid=#{Sid} and s_name=#{Sname}

    就是在原来方法的基础上增加了 flushCache= true ,这个属性配置为 true 后,在查询数据后会清空当前的一级缓存,因此调用该方法后每次都会重新从数据库中查询数据,但是由于这个方法清空了一级缓存,会影响当前 SqlSession 中所有缓存的查询,因此在需要反复查询获取只读数据的情况下,会增加数据库的查询次数,所以要避免这么使用。

    除了上面讲的将 flushCache赋值为true的情况外,还会导致一级缓存清空的情况就是关闭第一个 SqlSession,然后重新开启一个SqlSession,由于一级缓存是和 SqlSession 绑定的,只存在于 SqlSession的生命周期中,所以在新的SqlSession中调用刚才的方法,在缓存中就查不到,必须去数据库中查询,当然之后在调用过该方法并不清除的情况下就可以在缓存中取到了。

    一级缓存原理图:

    代码:

    public class MyBatisTest {

    public static void main( String[] args ) {

    SqlSession openSession = null;

    try {

    //mybatis配置文件

    String resourse="mybatis-cfg.xml";

    //通过 Resources 工具类将 ti -config.xm 配置文件读入 Reader

    InputStream inputStream=Resources.getResourceAsStream(resourse);

    //通过 SqlSessionFactoryBuilder 建造类使用 Reader 创建 SqlSessionFactory工厂对象

    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

    //通过SqlSessionFactory工厂得到SqlSession

    openSession = sqlSessionFactory.openSession();

    //通过反射机制来获取对应的Mapper实例

    StudentMapper mapper=openSession.getMapper(StudentMapper.class);

    Student student1=mapper.selectStudentByIdAndName(2,"danghh");

    Student student2=mapper.selectStudentByIdAndName(2,"danghh");

    System.out.println(student1);

    openSession.commit();

    } catch (IOException e) {

    e.printStackTrace();

    }finally {

    //最后一定不要忘记关闭 SqlSession ,否则会因为连接没有关闭导致数据库连接数过多,造成系统崩旗

    openSession.close();

    }

    }

    }

    运行结果:

    [DEBUG] - Setting autocommit to false on JDBC Connection[com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), danghh(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='danghh', Sage=22, Ssex='nv', course=null}

    Student{SID=2, Sname='danghh', Sage=22, Ssex='nv', course=null}

    通过结果可以看出,由于代码中查询是在一个SqlSession,且两次查询过程中没有更新信息,不会导致一级缓存失效,所以结果只进行了一次数据库查询。

    那如果是在两个SqlSession中分别进行查询呢?

    代码:

    结果:

    [DEBUG] - Opening JDBC Connection

    [DEBUG] - Checked out connection 234698513 from pool.

    [DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), danghh(String)

    [DEBUG] - <== Total: 1

    [DEBUG] - Opening JDBC Connection

    [DEBUG] - Created connection 1836797772.

    [DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6d7b4f4c]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), danghh(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='danghh', Sage=22, Ssex='nv', course=null}

    Student{SID=2, Sname='danghh', Sage=22, Ssex='nv', course=null}

    可以看出在使用了两个SqlSession进行查询后,在数据库进行了两次查询,也验证了SqlSession的一级缓存是和SqlSession的生命周期绑定的,作用范围也只在当前SqlSession中。

    另外在演示下如果在进行查询之前进行了一次update操作会不会使一级缓存清空呢?

    代码:

    运行结果:

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), hjj(String)

    [DEBUG] - <== Total: 1

    [DEBUG] - ==> Preparing: update student set S_name=?,Sage=?,Ssex=? where Sid=?

    [DEBUG] - ==> Parameters: hjj(String), 23(Integer), null, 2(Integer)

    [DEBUG] - <== Updates: 1

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), hjj(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    二,MyBatis二级缓存(全局缓存)

    MyBatis二级缓存非常强大,它不同于一级缓存只存在于 SqlSession 的生命周期中, 而是可以理解为存在于 SqlSessionFactory 的生命周期中 ,是Mapper(studentMapper) 级别的缓存,一个Mapper对应一个二级缓存,当Mapper中的多个SqlSession共同操作同一个方法时,多个SqlSession是可以共用二级缓存的中的数据的,所以二级缓存是跨SqlSession的。

    在开启二级缓存时,查出来的数据默认先存储在一级缓存中,当有 SqlSession关闭 时,它里面一级缓存中的数据就会被存储到Mapper的二级缓存中,这样该Mapper中的其他会话执行了相同方法时,就会在二级缓存中找到匹配的数据,如果没有找到,才会去数据库中查找。注意只有在该会话关闭时,它一级缓存中的数据才会被刷到二级缓存中。另外如果只是开启二级缓存的全局(config)开关,而会话(student)没有开启二级缓存,查询时也不会在二级缓存中查询。

    一级缓存( 也叫本地缓存)一般默认会启开,不需要进行配置,但要使用二级缓存就需要进行配置。 那如何配置呢?

    第一步 :在全局配置文件中添加

    (这个参数是二级缓存的全局开关,默认值是 true ,初始状态为启用状态,所以也可忽略此步的配置)

    (由于MyBatis二级缓存和命名空间namespace是绑定的 ,即二级缓存还需要在 Mapper.xml 映射文件中配置或者在 Mapper.java 接口中配置。)

    第二步 :在Sql映射文件中添加元素。

    上面的配置创建了一个 FIFO 缓存,并每隔6秒刷新一次,存储集合或对象的1024个引用,而且返回的对象被认为是非只读的。

    eviction :缓存的收回策略LRU (最近最少使用的) 移除最长时间不被使用的对象,这是默认值

    FIFO (先进先出〉 按对象进入缓存的顺序来移除它们

    SOFT (软引用) 移除基于垃圾回收器状态和软引用规则的对象

    WEAK (弱引用) 更积极地移除基于垃圾收集器状态和弱引用规则的对象

    flushinterval :刷新间隔

    设置缓存多长时间清空一次,单位为毫秒值,默认不清空。

    readOnly:是否只读

    true:只读,设置为true后,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据,因此为了加快获取速度,一般会直接将数据在缓存中的引用交给用户,虽然速度快,但不安全。

    false:非只读,设置为false后,mybatis认为获取的数据可能会被修改,因此会利用序列化和反序列化的技术克隆一份新的数据给你,虽然速度慢,但安全。

    默认是 false

    size :引用数目

    设置缓存可以存放的引用数目,可以被设置为任意正整数 。默认值是 1024。

    第三步 :给POJO类实现序列化接口

    代码:

    public class MyBatisTest {

    public static void main( String[] args ) {

    SqlSession openSession1 = null;

    SqlSession openSession2 = null;

    try {

    //mybatis配置文件

    String resourse="mybatis-cfg.xml";

    //通过 Resources 工具类将 ti -config.xm 配置文件读入 Reader

    InputStream inputStream=Resources.getResourceAsStream(resourse);

    //通过 SqlSessionFactoryBuilder 建造类使用 Reader 创建 SqlSessionFactory工厂对象

    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

    //通过SqlSessionFactory工厂得到SqlSession1

    openSession1 = sqlSessionFactory.openSession();

    StudentMapper mapper1=openSession1.getMapper(StudentMapper.class);

    //通过SqlSessionFactory工厂得到SqlSession2

    openSession2 = sqlSessionFactory.openSession();

    StudentMapper mapper2=openSession2.getMapper(StudentMapper.class);

    //使用会话1进行查询,此次查询结果只会存储在一级缓存中

    Student student1=mapper1.selectStudentByIdAndName(2,"hjj");

    System.out.println(student1);

    //使用会话2进行查询,前面会话未关闭,数据不会被刷到二级缓存中,所以本次仍会执行sql

    Student student2=mapper2.selectStudentByIdAndName(2,"hjj");

    System.out.println(student2);

    //使用会话2进行查询,由于前面已执行过该方法,所以可在一级缓存中查到

    Student student3=mapper2.selectStudentByIdAndName(2,"hjj");

    System.out.println(student3);

    openSession1.commit();

    } catch (IOException e) {

    e.printStackTrace();

    }finally {

    //最后一定不要忘记关闭 SqlSession ,否则会因为连接没有关闭导致数据库连接数过多,造成系统崩旗

    openSession1.close();

    }

    }

    }

    运行结果:

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.0

    [DEBUG] - Opening JDBC Connection

    [DEBUG] - Checked out connection 234698513 from pool.

    [DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), hjj(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.0

    [DEBUG] - Opening JDBC Connection

    [DEBUG] - Created connection 1843368112.

    [DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6ddf90b0]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), hjj(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.0

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - Returned connection 234698513 to pool.

    该颜色: 表示会话1第一次查询的结果,由于第一次查询,一级缓存和二级缓存中都没有数据,所以Mapper命中率为0.0,且进行了数据库查询,并将结果存储到会话1一级缓存中。

    该颜色: 表示会话2第一次查询的结果,由于会话1没有关闭,所以会话1的一级缓存不会刷到Mapper的二级缓存中,并且是在会话2中第一次查询该方法,所以Mapper命中率为0.0,且进行了数据库查询,并将结果存储到会话2的一级缓存中。

    该颜色: 表示会话2第二次查询的结果,虽然会话1没有关闭,会话1的一级缓存不会刷到Mapper的二级缓存中,但是在会话2中查询过该方法,在会话2的一级缓存中已存在该数据,所以Mapper命中率为0.0,没有进行数据库查询。

    接下来就验证下会话中一级缓存的数据是不是只有在该会话关闭后才会被刷新到mapper的二级缓存!

    代码:(仅截取部分)

    运行结果:

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.0

    [DEBUG] - Opening JDBC Connection

    [DEBUG] - Checked out connection 234698513 from pool.

    [DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - ==> Preparing: select * from student where sid=? and s_name=?

    [DEBUG] - ==> Parameters: 2(Integer), hjj(String)

    [DEBUG] - <== Total: 1

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.0

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@dfd3711]

    [DEBUG] - Returned connection 234698513 to pool.

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.3333333333333333

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    [DEBUG] - Cache Hit Ratio [MyBatisDemo.StudentMapper]: 0.5

    Student{SID=2, Sname='hjj', Sage=23, Ssex='null', course=null}

    展开全文
  • 一级缓存与二级缓存

    2021-02-27 16:45:51
    1.理解mybatis的缓存如大部分的持久化框架,mybatis也提供了一级缓存与二级缓存。一级缓存:基于perpetualcache的hashmap本地缓存,其存储作用为session,当session进行flush或者close之后,将该session中的cache...

    1.理解mybatis的缓存

    如大部分的持久化框架,mybatis也提供了一级缓存与二级缓存。

    一级缓存:基于perpetualcache的hashmap本地缓存,其存储作用为session,当session进行flush或者close之后,将该session中的cache进行清空。

    二级缓存:机制与一级缓存类似,默认采用perpetualcache,hashmap存储,不同在于存储作用域为Mapper(namespace),并且可以自定义存储。

    对于存储数据更新机制,当某一个作用域进行了C/U/D之后,默认该作用域下所有select中的缓存将被clear。

    一:一级缓存

    1.需求,目录结构

    根据id查询对应的用户记录对象。

    a940d8dd19c80faadd6848f39b9421db.png

    2.准备数据

    1 CREATE TABLEc_user(2 id INT PRIMARY KEYauto_increment,3 name VARCHAR(20),4 age INT

    5 );6 INSERT INTO c_user(name,age) VALUES("TOM",12);7 INSERT INTO c_user(NAME,age) VALUES("BOB",18);

    3.创建实体类

    1 packagecom.cao.bean;2

    3 public classCUser {4 private intid;5 privateString name;6 private intage;7 publicCUser() {}8 public CUser(int id,String name,intage) {9 this.id=id;10 this.name=name;11 this.age=age;12 }13 public intgetId() {14 returnid;15 }16 public void setId(intid) {17 this.id =id;18 }19 publicString getName() {20 returnname;21 }22 public voidsetName(String name) {23 this.name =name;24 }25 public intgetAge() {26 returnage;27 }28 public void setSex(intage) {29 this.age =age;30 }31 @Override32 publicString toString() {33 return "CUser [id=" + id + ", name=" + name + ", age=" + age + "]";34 }35

    36

    37 }

    4.映射文件

    1 <?xml version="1.0" encoding="UTF-8"?>

    2 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    5

    6

    7 select * from c_user where id=#{id}8

    9

    10 update c_user set11 name=#{name},age=#{age} where id=#{id}12

    13

    5.配置文件

    1 <?xml version="1.0" encoding="UTF-8"?>

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    6.测试类

    1 packagecom.jun.main;2

    3 importjava.io.IOException;4 importjava.io.Reader;5 importjava.util.HashMap;6 importjava.util.Map;7

    8 importorg.apache.ibatis.io.Resources;9 importorg.apache.ibatis.session.SqlSession;10 importorg.apache.ibatis.session.SqlSessionFactory;11 importorg.apache.ibatis.session.SqlSessionFactoryBuilder;12 importorg.junit.Test;13

    14 importcom.cao.bean.CUser;15 public classMainTest {16 /**

    17 * 方式一18 *@throwsException19 */

    20 @Test21 public void test1() throwsException {22 Reader reader=Resources.getResourceAsReader("com/cao/config/Configuration.xml");23 SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);24 SqlSession sqlSession=sqlSessionFactory.openSession(true); //true后是自动提交

    25

    26 String statement="procedure.getUser";27

    28 CUser cuser=sqlSession.selectOne(statement, 1);29 System.out.println(cuser);30 //再跑一次

    31 cuser=sqlSession.selectOne(statement, 1);32 System.out.println(cuser);33 sqlSession.close();34 }35

    36

    37 }

    7.效果

    说明一级缓存是开着的。

    759df211ae1a0d256d9439661e0291e1.png

    8.调用方法进行清空缓存

    sqlSession.clearCache();

    1 packagecom.jun.main;2

    3 importjava.io.IOException;4 importjava.io.Reader;5 importjava.util.HashMap;6 importjava.util.Map;7

    8 importorg.apache.ibatis.io.Resources;9 importorg.apache.ibatis.session.SqlSession;10 importorg.apache.ibatis.session.SqlSessionFactory;11 importorg.apache.ibatis.session.SqlSessionFactoryBuilder;12 importorg.junit.Test;13

    14 importcom.cao.bean.CUser;15 public classMainTest {16 /**

    17 * 方式一18 *@throwsException19 */

    20 @Test21 public void test1() throwsException {22 Reader reader=Resources.getResourceAsReader("com/cao/config/Configuration.xml");23 SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);24 SqlSession sqlSession=sqlSessionFactory.openSession(true); //true后是自动提交

    25

    26 String statement="procedure.getUser";27

    28 CUser cuser=sqlSession.selectOne(statement, 1);29 System.out.println(cuser);30 //再跑一次

    31 cuser=sqlSession.selectOne(statement, 1);32 System.out.println(cuser);33 //清空缓存

    34 sqlSession.clearCache();35 cuser=sqlSession.selectOne(statement, 1);36 System.out.println(cuser);37

    38 sqlSession.close();39 }40

    41

    42 }

    9.效果

    647999a2925ed5a75c1e5aa46b7e52a4.png

    10.增删改后清空缓存

    1 packagecom.jun.main;2

    3 importjava.io.IOException;4 importjava.io.Reader;5 importjava.util.HashMap;6 importjava.util.Map;7

    8 importorg.apache.ibatis.io.Resources;9 importorg.apache.ibatis.session.SqlSession;10 importorg.apache.ibatis.session.SqlSessionFactory;11 importorg.apache.ibatis.session.SqlSessionFactoryBuilder;12 importorg.junit.Test;13

    14 importcom.cao.bean.CUser;15 public classMainTest {16 /**

    17 * 方式一18 *@throwsException19 */

    20 @Test21 public void test1() throwsException {22 Reader reader=Resources.getResourceAsReader("com/cao/config/Configuration.xml");23 SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);24 SqlSession sqlSession=sqlSessionFactory.openSession(true); //true后是自动提交

    25

    26 String statement="procedure.getUser";27

    28 CUser cuser=sqlSession.selectOne(statement, 1);29 System.out.println(cuser);30 //再跑一次

    31 cuser=sqlSession.selectOne(statement, 1);32 System.out.println(cuser);33 //清空缓存34 //sqlSession.clearCache();35 //执行增删改操作

    36 String statement2="procedure.updateUser";37 int update=sqlSession.update(statement2, new CUser(1,"JACK",15));38 sqlSession.commit();39 System.out.println(update);40 //再次查询

    41 cuser=sqlSession.selectOne(statement, 1);42 System.out.println(cuser);43

    44 sqlSession.close();45 }46

    47

    48 }

    11.效果

    c9fbd19c7b739e6e8a4f1e02134ef8b6.png

    12.关闭session清空缓存

    二:二级缓存

    1.添加一个<>cache>在映射文件中

    2.新建对象(这个对象必须实现序列化接口Serializable)

    注意点。

    1 packagecom.cao.bean;2

    3 importjava.io.Serializable;4

    5 public class CUser implementsSerializable{6 private intid;7 privateString name;8 private intage;9 publicCUser() {}10 public CUser(int id,String name,intage) {11 this.id=id;12 this.name=name;13 this.age=age;14 }15 public intgetId() {16 returnid;17 }18 public void setId(intid) {19 this.id =id;20 }21 publicString getName() {22 returnname;23 }24 public voidsetName(String name) {25 this.name =name;26 }27 public intgetAge() {28 returnage;29 }30 public void setSex(intage) {31 this.age =age;32 }33 @Override34 publicString toString() {35 return "CUser [id=" + id + ", name=" + name + ", age=" + age + "]";36 }37

    38

    39 }

    3.在映射文件中加一个cache标签

    1 <?xml version="1.0" encoding="UTF-8"?>

    2 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    5

    6

    7

    8 select * from c_user where id=#{id}9

    10

    11 update c_user set12 name=#{name},age=#{age} where id=#{id}13

    14

    4.测试类

    1 packagecom.jun.main;2

    3 importjava.io.IOException;4 importjava.io.Reader;5 importjava.util.HashMap;6 importjava.util.Map;7

    8 importorg.apache.ibatis.io.Resources;9 importorg.apache.ibatis.session.SqlSession;10 importorg.apache.ibatis.session.SqlSessionFactory;11 importorg.apache.ibatis.session.SqlSessionFactoryBuilder;12 importorg.junit.Test;13

    14 importcom.cao.bean.CUser;15 public classMainTest {16 /**

    17 * 一18 *@throwsException19 */

    20 @Test21 public void test1() throwsException {22 Reader reader=Resources.getResourceAsReader("com/cao/config/Configuration.xml");23 SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);24 SqlSession sqlSession=sqlSessionFactory.openSession(true); //true后是自动提交

    25

    26 String statement="procedure.getUser";27

    28 CUser cuser=sqlSession.selectOne(statement, 1);29 System.out.println(cuser);30 //再跑一次

    31 cuser=sqlSession.selectOne(statement, 1);32 System.out.println(cuser);33 //清空缓存34 //sqlSession.clearCache();35 //执行增删改操作

    36 String statement2="procedure.updateUser";37 int update=sqlSession.update(statement2, new CUser(1,"JACK",15));38 sqlSession.commit();39 System.out.println(update);40 //再次查询

    41 cuser=sqlSession.selectOne(statement, 1);42 System.out.println(cuser);43

    44 sqlSession.close();45 }46 /**

    47 * 二48 *@throwsException49 */

    50 @Test51 public void test2() throwsException {52 Reader reader=Resources.getResourceAsReader("com/cao/config/Configuration.xml");53 SqlSessionFactory sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);54 SqlSession sqlSession1=sqlSessionFactory.openSession();55 SqlSession sqlSession2=sqlSessionFactory.openSession();56

    57 String statement="procedure.getUser";58

    59 CUser cuser=sqlSession1.selectOne(statement, 1);60 sqlSession1.commit();61 System.out.println(cuser);62 //再跑一次

    63 cuser=sqlSession2.selectOne(statement, 1);64 sqlSession1.commit();65 System.out.println(cuser);66

    67

    68 sqlSession1.close();69 }70

    71

    72 }

    5.效果

    e29387793467c2b2d21ddcf3c28e9263.png

    6.注意点

    如果第一个session没有提交,则没有进行二级缓存。

    当然,如果第一个session是自动提交,第二个session也没有进行二次缓存。

    所以,想实现二级缓存,需要前面的session已经提交过,并且相同的提交sql。

    三:回收策略

    1.策略

    LRU:最近最少使用,移除长时间不使用的对象

    FIFO:先进先出

    SOFT:软引用,移除基于垃圾回收状态和软引用规则的对象

    WEAK:弱引用,更积极的移除基于垃圾收集器状态和软引用规则的对象

    2.默认

    LRU。

    展开全文
  • 缓存是什么 缓存其实就是存储在内存中的临时数据,这里的数据量会比较小,一般来说,服务器的内存也是有限的,不可能将所有的数据都放到

    缓存是什么

         缓存其实就是存储在内存中的临时数据,这里的数据量会比较小,一般来说,服务器的内存也是有限的,不可能将所有的数据都放到服务器的内存里面,所以, 只会把关键数据放到缓存中,缓存因为速度快,使用方便而出名!

     

    为什么需要缓存

        BS架构里面,用户的所有操作都是对数据库的增删改查,其中查询的操作是最多的,但如果用户想要某个数据时每次都去数据库查询,这无疑会增加数据库的压力,而且获取时间效率也会降低,所以为了解决这些问题,缓存应用而生,使用了缓存之后,服务器只需要查询一次数据库,然后将数据保存到服务器主机的内存中,以后读取时就直接取内存中的数据,而不需要每次都查数据库,这种方案除了降低数据库压力之外,还提高了响应速度,简直一箭双雕哇~

     

    哪些数据会放到缓存

        通常情况下,都会将那些变化较少且经常用到的数据会放到缓存中,比如像字典、系统参数、有固定值的状态码等等;另外将用户保存到缓存也是一种很好的策略,这样登录的时候就可以极速响应了;

     

    mybatis一级缓存

    mybatis 的缓存分为2类,分别是一级缓存和二级缓存,一级缓存是默认开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中,一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的,所以我们可以认为每个请求都有自己的一级缓存,如果同一个sqlSession会话中2 个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空;

     Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
             // 。。。。。。 
            // 这中间所走的所有查询操作都会进行缓存,一旦关闭sqlSession会话,缓存则会刷新
    
            //释放会话
            sqlSession.clearCache();
            // 关闭会话
            sqlSession.close();

    一级缓存流程图

    我们来试验一把

    在mybatis 的配置文件中加入以下配置,开启sql日志,每一个sql代表请求了一次数据库,这样我们就可以根据sql来判断是否使用了缓存

      <settings>
            <!--标准的日志工厂实现类,打印sql日志-->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>

    示例代码

    public static void main(String[] args) throws IOException {
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            // 查询第一次
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
    
            // 第二次查询
            User user1 = mapper.selectByPrimaryKey("3rfrf34r34");
    
            System.out.println("两个user对象是否相等:"+(user == user1));
            //释放会话
            sqlSession.clearCache();
            sqlSession.close();
        }

    打印结果

     根据结果可以看到,代码中执行了2次查询, 但实际运行时只查询了一次数据库,第二次获取数据时直接从缓存中读取,并且2次读取的数据都是一样的,到这里,一级缓存就已经生效了;

     

    接下来我们来测试第二种情况 :查询   ->   修改  -> 查询

    实例代码

     public static void main(String[] args) throws IOException {
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            // 查询第一次
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
            
            // 修改
            mapper.updateByPrimaryKey(user);
    
            // 第二次查询
            User user1 = mapper.selectByPrimaryKey("3rfrf34r34");
    
            System.out.println("两个user对象是否相等:"+(user == user1));
            //释放会话
            sqlSession.clearCache();
            sqlSession.close();
        }

    打印结果

     

    控制台打印了三次sql,其中第一个查询和第三个查询是一样的,但是并没有使用缓存,为什么会这样呢?因为每次增删改操作都有可能会改变原来的数据,所以必须刷新缓存;

     

    二级缓存

        二级缓存是全局的,也就是说;多个请求可以共用一个缓存,二级缓存需要手动开启,有2种方式配置二级缓存,

    • 缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;
    • 开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找;

    二级缓存流程图

    第一种配置方式

    单个mapper配置,主需要在需要开启二级缓存的mapper.xml文件中加入以下配置即可开启

     <!-- 开启单个mapper的二级缓存,也叫全局缓存-->
      <cache />

    注意一定要加到xxMapper.xml的文件内,千万不要加到mybatis 的主配置文件里面了,会报错的

    第二种配置方式

    所有的mapper都开启二级缓存,在mybatis.xml中加入以下配置即可

     <settings>
            <!--  开启所有mapper的二级缓存 -->
            <!--<setting name="cacheEnabled" value="true" />-->
        </settings>

     

     

    示例代码

    
        public static void main(String[] args) throws IOException {
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
    
            // 第一个会话
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取会话一的mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            // 第一次查询
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
            
    
            //释放第一个会话
            sqlSession.clearCache();
            sqlSession.close();
    
            // 第二个会话
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
            // 获取会话二的mapper接口对象
            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    
            // 第二次查询
            User user1 = mapper2.selectByPrimaryKey("3rfrf34r34");
            // 释放第二个会话
            sqlSession2.clearCache();
            sqlSession2.close();
        }

     打印结果

    打印结果很明显,2次查询,但是日志显示只查询了一次数据库, 第二次是从缓存中获取的数据,至此,二级缓存已开启!

    注意事项

    另外,缓存还有以下几种情况需要注意

    • 映射语句文件中的所有 select 语句的结果将会被缓存。
    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
    • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
    • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
    • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
    • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

     

    展开全文
  • 一级缓存:也称本地缓存,sqlSession级别的缓存。一级缓存是一直开启的;与数据库同一次回话期间查询到的数据会放在本地缓存中。如果需要获取相同的数据,直接从缓存中拿,不会再查数据库。一级缓存失效的四种情况:...

    一级缓存:

    也称本地缓存,sqlSession级别的缓存。一级缓存是一直开启的;与数据库同一次回话期间查询到的数据会放在本地缓存中。

    如果需要获取相同的数据,直接从缓存中拿,不会再查数据库。

    一级缓存失效的四种情况:

    1.sqlSession不同。

    eg:

    @Test

    public void test01() throws IOException {

    SqlSessionFactory sqlSessionFactry = getSqlSessionFactory();

    SqlSession session = sqlSessionFactory.openSession();

    EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

    try { Employee map = mapper.getEmployeeById(1);

    Employee map2 = mapper.getEmployeeById(1);

    System.out.println(map == map2);

    session.commit();

    }

    finally { session.close(); }

    }

    输出结果为true 数据库只查询一次,map2取缓存结果

    @Test

    public void test01() throws IOException {

    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

    SqlSession session = sqlSessionFactory.openSession();

    SqlSession session2 = sqlSessionFactory.openSession();

    EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

    EmployeeMapper mapper2 = session.getMapper(EmployeeMapper.class);

    try {

    Employee map = mapper.getEmployeeById(1);

    Employee map2 = mapper.getEmployeeById(1);

    System.out.println(map == map2);

    session.commit();

    } finally {

    session.close();

    }

    }

    输出结果为false

    两个不同的sqlSession

    2.sqlSession相同,查询条件不同。因为缓存条件不同,缓存中还没有数据。

    3.sqlSession相同,在两次相同查询条件中间执行过增删改操作。(因为中间的增删改可能对缓存中数据进行修改,所以不能用)

    4.sqlSession相同,手动清空了一级缓存。

    eg:

    @Test

    public void test01() throws IOException {

    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

    SqlSession session = sqlSessionFactory.openSession();

    EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

    try {

    Employee map = mapper.getEmployeeById(1);

    session.clearCache();

    Employee map2 = mapper.getEmployeeById(1);

    System.out.println(map == map2);

    session.commit();

    } finally {

    session.close();

    }

    }

    输出结果为false.

    因为手动清清除缓存,缓存失效

    二级缓存:全局缓存;基于namespace级别的缓存。一个namespace对应一个二级缓存。

    工作机制:1.一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中。

    2,如果会话被关闭了,一级缓存中的数据会被保存带二级缓存。新的会话查询信息就会参照二级缓存。

    3.sqlSession ====> Employee====>employee

    sqlSession ====>DepartmentMapper=====>Department

    不同的namespace查出的数据会放在自己对应的缓存中。

    效果:查出的数据首先放在一级缓存中,只有一级缓存被关闭或者提交以后,一级缓存数据才会转移到二级缓存

    使用步骤:

    1.开启全局缓存配置。

    2.因为是namespace级别,需要搭配每个xxxMapper.xml中配置二级缓存

    eviction:缓存的回收策略:

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

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

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

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

    flushInterval:缓存刷新间隔。缓存多久清空一次,默认不清空。设置一个毫秒值。

    readOnly:是否只读。true:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。

    mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。

    false:mybatis觉得获取的数据可能被修改。mybatis会利用序列化和反序列化的技术克隆一份新的数据给用户。安全,速度快。

    size:缓存放多少元素。

    type:指定自定义缓存全类名。实现cache接口即可。

    3.pojo需要实现序列换接口。

    和缓存相关的配置/属性:

    1.cacheEnabled:如果是false,关闭二级缓存,不关闭一级缓存。

    2.每个select标签都有userCache="true"属性:对一级缓存没有影响。设置为false,二级缓存失效。

    3.每个增删改标签都有flushCache="true"属性:一级缓存和二级缓存都会被清空。

    4.在查询标签中flushCache="false"属性:如果设置为true,查完会清空,一级二级缓存都会被清空,都不会用缓存。

    5.sqlSession.clearn():跟session有关,只会清除一级缓存。

    6.localCacheScope:本地缓存作用域。

    一级缓存SESSION:当前会话的所有数据保存到回话缓存中。STATEMENT:禁用一级缓存。

    缓存首先一进来去查二级缓存,二级缓存没有去找一级缓存,一级缓存没有去找数据库。二级缓存----->一级缓存-------->数据库。

    自定义缓存 implements Cache,重写接口中的保存等方法,比如说保存到redis.

    自定义缓存参照mybatis官网--->项目git代码库----->https://github.com/mybatis---->参照各种整合缓存

    原文:http://www.cnblogs.com/maoyizhimi/p/7778504.html

    展开全文
  • 一级缓存Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法...
  • 一级缓存一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作...
  • CPU是块超大规模的集成电路,是台计算机的运算核心(Core)和控制核心(Control Unit);CPU功能主要是解释计算机指令以及处理计算机软件中的数据;中央处理器主要包括运算器(算术逻辑运算单元,ALU,Arithmetic ...
  • 本文主要介绍在SpringBoot项目中如何使用Mybatis的一级、二级缓存,为了演示方便,本文的数据库采用H2内存数据库,数据库连接池默认使用SpringBoot2.X自带的hikariCP。 正确的使用Mybatis缓存可以有效减少多余的...
  • 缓存的大小和结构对CPU速度的影响更大,因此缓存的大小也是CPU的重要性能指标之. CPU缓存的工作效率可以远远超过内存和硬盘的速度. 以下安装主页主要介绍有关CPU缓存的知识,有兴趣的用户可能希望学习.什么是CPU...
  • 一、什么是缓存 缓存是存在于内存中的临时数据。 使用缓存减少和数据库的交互...一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第
  • 文章目录缓存介绍一级缓存一级缓存失效的原因二级缓存二级缓存使用步骤缓存相关属性缓存使用顺序二级缓存的使用原则缓存介绍MyBatis中使用缓存来提高其性能。当查询数据时, 会先从缓存中取出数据,如果缓存中没有,再...
  • Mybatis 一级缓存和二级缓存

    千次阅读 2021-01-15 01:01:56
    本文的目的则是向读者详细介绍MyBatis的一级缓存,深入源码,解析MyBatis一级缓存的实现原理,并且针对一级缓存的特点提出了在实际使用过程中应该注意的事项。 一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存...
  • 一级缓存:也称本地缓存,sqlSession级别的缓存。一级缓存是一直开启的;与数据库同一次会话期间查询到的数据会放在本地缓存中。如果需要获取相同的数据,直接从缓存中拿,不会再查数据库。一级缓存失效的四种情况:...
  • 一级缓存 Mybatis支持缓存,默认情况下是开启的一级缓存一级缓存是SqlSession级别,缓存的是SqlSession对象。在SQL语句、参数都相同的情况下,我们使用同一个SqlSession对象调用同一个Mapper方法的时候,只需要...
  • Mybatis一级缓存(默认开启) 是SqlSession级别的缓存, 默认开启 Mybatis二级缓存(默认关闭,一般不建议使用) namspace/mapper级别(跨sqlSession)的缓存, 默认关闭
  • Mybatis一级缓存和二级缓存的区别 缓存原理图: 一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,...
  • 在mybatis-coinfig.xml文件添加如下配置: <configuration> <settings> <!...-- 设置一级缓存的作用范围是一条sql语句执行期间,执行完毕清空一级缓存--> <setting name=
  • 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是个学习Java半年多时间的小菜鸟,同时还有个伟大的梦想,那就是有朝一日,成为个优秀的Java架构师。 这个Mybatis基础学习系列是用来记录我...
  • 一级缓存 (session级别) 1.同一个session; 2.默认开启 一级缓存失效的四种情况: 1、sqlsession不同 2、sqlsession相同,查询缓存中没有的数据 3、sqlsession相同,但两次查询之间执行了增删改操作 4、sqlsession...
  • MYSQL面试篇一之mysql的一级缓存和二级缓存 一级缓存一级缓存也称本地缓存,session级别的缓存,一级缓存是默认开启的,与数据库同一次的会话期间查询到的数据会放在本地缓存中,如果有需要获取相同的数据,则...
  • 题记 常常听到别人提起:“一级缓存、二级缓存、三级缓存”。那么它们是什么呢?有什么作用呢?
  • 如题,Spring整合MyBatis为什么会导致MyBatis的一级缓存失效? 一、搭建失效场景 测试用例为,查询一个String类型的字符串 基础的环境配置、扫描路径配置就不再重复,直接看测试数据相关的代码信息 1、对应的...
  • 一、MyBatis一级缓存1.MyBatis一级缓存默认是开启的2.流程:3.MyBatis命中缓存原则1.StatmentId必须一致,否则无法命中2.传递给SQL的参数必须一致,比如,传入的map中有两个参数,只要传入的SQL中的id值是一样的,...
  • 对于mybatis的缓存,我们往往有这样两个疑问:一级缓存、二级缓存的过期时间是多少?后台是否有个线程在检测?针对这两个问题,见下面的分析:1、一级缓存无过期时间,只有生命周期(1)MyBatis在开启一个数据库会话时...
  • 1.可以知道的是Mybatis的一级缓存SqlSession默认是开启的。 2.可以自己去开启二级缓存在config.xml文件中的properties下面可以加上这个配置: <settings> <!--进行懒加载--> <setting name=...
  • MyBatis一级缓存和二级缓存使用场景和失效策略 一级缓存: 默认为一级缓存,作用域为SqlSessison,基于PerpetualCache的HashMap本地缓存。 实现方式:mysql第一次去数据库查数据的时候会往sqlsession里面写一份,第...
  • MyBatis的一级缓存和二级缓存有什么区别? MyBatis的一级缓存:该缓存仅仅只会对sqlSession数据查询数据进行缓存,缓存至一个map中,生命周期很短,当sqlSession 消失时候消失,一级缓存是默认开启的,,每次的增...
  • 一级缓存: 基于sqlsession级别的缓存,默认是开启状态,基本可以理解为namespace空间下的同一条sql语句,分别的执行了2次(在spring同一个事物中,不在同一事物会失效),然后命中后,一级缓存生效。 二级缓存: ...
  • Talk is cheap, Show ... * 一级缓存的作用范围session */ try (SqlSession session = sqlSessionFactory.openSession()) { User user = new User(); user.setId(6); user.setName("test6"); int i = session.in

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 663,655
精华内容 265,462
关键字:

一级缓存

友情链接: GPAcalactor.rar