Mybatis一级缓存 二级缓存

Mybatis缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

一级缓存(local cache)

**一级缓存, 即本地缓存, 作用域默认为sqlSession。**当Session flush或close 后,该Session中的所有Cache将被清空。 本地缓存不能被关闭, 但可以调用clearCache() 来清空本地缓存, 或者改变缓存的作用域。在mybatis3.1之后, 可以配置本地缓存的作用域,在mybatis.xml 中配置 。同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中,key:hashCode+查询的SqlId+编写的sql查询语句+参数

UserMapper
public interface UserMapper {
    public User findById(int id);
}
UserMapper.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">

<mapper namespace="com.itheima.dao.UserMapper">

    <select id="findById" parameterType="int" resultType="user">
        select * from user where id= #{id}
    </select>
</mapper>
User.java
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User  {

    private Integer id;
    private String username;
    private String password;
    private String birthday;
/*  省略 get  set方法   以及toString方法*/
}
SqlMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <typeAliases>
        <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>
    </environments>
    <mappers>
       
        <!--使用package扫描时,包名必须一致,类名和映射文件不区分大小写 Spring整合环境下必须一致-->
        <package name="com.itheima.dao"></package>
    </mappers>

</configuration>
测试类
    @Test
    public void test1() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();


        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user1 = userMapper.findById(1);
        System.out.println(user1);

        User user2 = userMapper.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);

        sqlSession.close();

    }

日志显示两次查询,只查询了一次数据库

16:17:46,559 DEBUG ResolverUtil:256 - Checking to see if class com.itheima.dao.UserMapper matches criteria [is assignable to Object]
16:17:46,856 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
16:17:46,868 DEBUG JdbcTransaction:137 - Opening JDBC Connection
16:17:47,496 DEBUG PooledDataSource:406 - Created connection 599491651.
16:17:47,497 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:17:47,504 DEBUG findById:159 - ==> Preparing: select * from user where id= ?
16:17:47,551 DEBUG findById:159 - > Parameters: 1(Integer)
16:17:47,611 DEBUG findById:159 - <
Total: 1

User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
16:17:47,612 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
true
16:17:47,624 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:17:47,625 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:17:47,625 DEBUG PooledDataSource:363 - Returned connection 599491651 to pool.

一级缓存失效的四种情况

(未 close SqlSession情况下)

1、不同的SqlSession对应不同的一级缓存

    @Test
    public void test3() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();


        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

        User user2 = userMapper2.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);

        sqlSession1.close();
        sqlSession2.close();

    }
2、同一个SqlSession但是查询条件不同
   @Test
    public void test2() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user1 = userMapper.findById(1);
        System.out.println(user1);

        User user2 = userMapper.findById(2);
        System.out.println(user2);

        System.out.println(user1==user2);

        sqlSession.close();

    }
3、同一个SqlSession两次查询期间执行了任何一次增删改操作
@Test
    public void test4() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

        User user=new User();
        user.setId(3);
        user.setPassword("6789");
        userMapper1.updataById(user);

        User user2 = userMapper1.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);

        sqlSession1.close();

    }
4. 清除本地缓存
     @Test
    public void test4() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

 	    sqlSession1.clearCache();

        User user2 = userMapper1.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);

        sqlSession1.close();

    }

二级缓存

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

工作机制:

1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容;
不同namespace查出的数据会放在自己对应的缓存中(map),查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中。

使用方式:

1)、开启全局二级缓存配置:

sqlMapper.xml

<setting name="cacheEnabled" value="true"/>
2)、去mapper.xml中配置使用二级缓存: 只有在哪个mapper下面配置下面的,才会用到二级缓存,否则即使开启二级全局缓存,二级缓存也不生效

(放在接口对应的xml文件里, 比如查User对象 就放在UserMapper.xml) <cache/>

3)POJO需要实现序列化接口 (实现 serialize接口)
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的
readOnly设置为false 实体类必须实现Serializable序列化接口,否则报错
Caused by: java.io.NotSerializableException: com.itheima.domain.User1
原因:通过序列化 返回缓存对象的拷贝
cache标签可以配置的属性:
eviction:缓存的回收策略:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。

flushInterval:缓存刷新间隔 缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。速度快安全
false:非只读:通过序列化 返回缓存对象的拷贝。安全,速度慢 默认值是 false。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

扫描二维码关注公众号,回复: 9605499 查看本文章

二级缓存代码示例

1)核心配置文件里配置  <setting name="cacheEnabled" value="true"/>
2)public class Person implements Serializable{...}
3)mapper映射文件加入<cache></cache>
sqlMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <typeAliases>
        <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>
    </environments>
    <mappers>  
        <!--使用package扫描时,包名必须一致,类名和映射文件不区分大小写 Spring整合环境下必须一致-->
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>
User.java
package com.itheima.domain;
​
import java.io.Serializable;
import java.util.Date;
import java.util.List;public class User implements Serializable {private Integer id;
    private String username;
    private String password;
    private String birthday;
​
    省略 set get方法 以及tostring方法
}
​UserMapper.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"><mapper namespace="com.itheima.dao.UserMapper">
     <cache/>
    <select id="findById" parameterType="int" resultType="user">
        select * from user where id= #{id}
    </select>

</mapper>

解释:sqlSession1查询结果之后,关闭sqlSession1,会将结果写入二级缓存,然后sqlSession2查询会从二级缓存中查询,不从数据查询数据了。下面日志可以证明:

@Test
    public void testn() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

        sqlSession1.close();

        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user2 = userMapper2.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);
    }

16:45:19,189 DEBUG ResolverUtil:256 - Checking to see if class com.itheima.dao.UserMapper matches criteria [is assignable to Object]
16:45:19,416 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
16:45:19,428 DEBUG JdbcTransaction:137 - Opening JDBC Connection
16:45:19,979 DEBUG PooledDataSource:406 - Created connection 599491651.
16:45:19,980 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:45:19,983 DEBUG findById:159 - ==> Preparing: select * from user where id= ?
16:45:20,018 DEBUG findById:159 - > Parameters: 1(Integer)
16:45:20,060 DEBUG findById:159 - <
Total: 1
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
16:45:20,078 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:45:20,079 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:45:20,079 DEBUG PooledDataSource:363 - Returned connection 599491651 to pool.
16:45:20,082 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.5
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
false

注意:只有一级缓存关闭的情况下二级缓存才会生效,下面演示中一级缓存没有关闭,二级缓存没有起作用,注意sqlSession1.close()的位置

@Test
    public void testn() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();


        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);
		//关闭一级缓存 二级缓存生效
  		//sqlSession1.close();

        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user2 = userMapper2.findById(1);
        System.out.println(user2);

        System.out.println(user1==user2);
		sqlSession1.close();
		sqlSession2.close();

    }

查询了两次数据库

16:47:44,213 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
16:47:44,225 DEBUG JdbcTransaction:137 - Opening JDBC Connection
16:47:44,658 DEBUG PooledDataSource:406 - Created connection 599491651.
16:47:44,659 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
16:47:44,662 DEBUG findById:159 - ==> Preparing: select * from user where id= ?
16:47:44,698 DEBUG findById:159 - > Parameters: 1(Integer)
16:47:44,752 DEBUG findById:159 - <
Total: 1
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
16:47:44,754 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
16:47:44,754 DEBUG JdbcTransaction:137 - Opening JDBC Connection
16:47:44,762 DEBUG PooledDataSource:406 - Created connection 1427889191.
16:47:44,762 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@551bdc27]
16:47:44,763 DEBUG findById:159 - ==> Preparing: select * from user where id= ?
16:47:44,763 DEBUG findById:159 - > Parameters: 1(Integer)
16:47:44,764 DEBUG findById:159 - <
Total: 1
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
false

验证二级缓存 存储对象

@Test
    public void testn() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapper.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

        User1 user3 = userMapper1.findById1(1);
        System.out.println(user3);
        //关闭sqlSession1 关闭一级缓存
//		这个时候 数据就应该存到二级缓存    但是因为配置文件里 二级缓存的size大小只给了1  所以只存进去了 User的对象 
//		User1的对象就没存进去  也就是说下面查User用了缓存  查User1没有用缓存
        sqlSession1.close();


//        验证二级缓存
        User user2 = userMapper2.findById(1);
        System.out.println(user2);


        User1 user4 = userMapper2.findById1(1);
        System.out.println(user4);

//        System.out.println(user1==user2);

    }

User使用了缓存,User1没有使用缓存

21:03:42,674 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
21:03:42,677 DEBUG findById:159 - ==> Preparing: select * from user where id= ?
21:03:42,722 DEBUG findById:159 - > Parameters: 1(Integer)
21:03:42,742 DEBUG findById:159 - <
Total: 1
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
21:03:42,743 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.0
21:03:42,743 DEBUG findById1:159 - ==> Preparing: select username from user where id= ?
21:03:42,743 DEBUG findById1:159 - > Parameters: 1(Integer)
21:03:42,745 DEBUG findById1:159 - <
Total: 1
User1{username=‘lucy’}
21:03:42,753 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
21:03:42,754 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
21:03:42,754 DEBUG PooledDataSource:363 - Returned connection 599491651 to pool.
21:03:42,756 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.3333333333333333
User{id=1, username=‘lucy’, password=‘123’, birthday=‘2018-12-12’, birthday=null, names=null}
21:03:42,756 DEBUG UserMapper:62 - Cache Hit Ratio [com.itheima.dao.UserMapper]: 0.25
21:03:42,757 DEBUG JdbcTransaction:137 - Opening JDBC Connection
21:03:42,757 DEBUG PooledDataSource:398 - Checked out connection 599491651 from pool.
21:03:42,758 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@23bb8443]
21:03:42,764 DEBUG findById1:159 - ==> Preparing: select username from user where id= ?
21:03:42,765 DEBUG findById1:159 - > Parameters: 1(Integer)
21:03:42,766 DEBUG findById1:159 - <
Total: 1
User1{username=‘lucy’}

UserMapper.xml
  <cache
            eviction="FIFO"
            flushInterval="60000"
            size="1"
            readOnly="false"
    />
User1
import java.io.Serializable;public class User1 implements Serializable {
    private String username;public String getUsername() {
        return username;
    }public void setUsername(String username) {
        this.username = username;
    }
​
    @Override
    public String toString() {
        return "User1{" +
                "username='" + username + '\'' +
                '}';
    }
}
发布了31 篇原创文章 · 获赞 8 · 访问量 1529

猜你喜欢

转载自blog.csdn.net/qq_37126480/article/details/103708545