Mybatis - 缓存

1. 概述

与大多数持久层框架一下,Mybatis同样提供了一级缓存和二级缓存的支持。

  • 一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session做flush或close之后,该Session中的所有cache就将清空;
  • 二级缓存与一级缓存机制相同,默认也是采用PerpetualCache的HashMap存储,不同在于其存储作用域为Mapper(NameSpace),并且可自定义存储源,如Ehcache。

注意:对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存NameSpace)进行了CUD操作后,默认该作用域下所有select中的缓存将被clear。

2. 准备工作

创建maven工程,目录结构如下:

user表结构及数据:

User类:

public class User {
    private int id;
    private String user_name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUser_name() {
        return user_name;
    }
    public void setUser_name(String user_name) {
        this.user_name = user_name;
    } 
    
}  

UserMapper类:

public interface UserMapper {
 
    User selectUserById(int id);
    User selectUserByName(String name);
}  

mybatis-config.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>
  <properties resource="jdbc.properties" />
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${pwd}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/mapper/UserMapper.xml"/>
  </mappers>
</configuration>

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="org.mybatis.mapper.UserMapper">
  <select id="selectUserById" resultType="org.mybatis.mapper.User">
    select * from user where id = #{id}
  </select>
  
   <select id="selectUserByName" parameterType="java.lang.String" resultType="org.mybatis.mapper.User">
    select * from user where user_name = #{name};
  </select>
</mapper>

App类:

public class App {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.selectUserById(1);
            System.out.println(user.getUser_name());
            User u = mapper.selectUserByName("xiaoli");
            System.out.println(u.getId());
            session.commit();
        } finally {
            session.close();
        }
    }
}  

pom.xml文件增加如下依赖:

<dependencies>
  <dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.4.4</version>
  </dependency>
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.36</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>1.7.22</version>
  </dependency>

 </dependencies>

 

3. 一级缓存

mybatis会在标示会话的SqlSession对象中建立一个简单的本地缓存(local cache),每次查询到的结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,不需要再进行一次数据库查询了。

一级缓存是SqlSession级别的缓存,在操作数据库时需要构造SqlSession,在对象中有一个内存区域数据结构HashMap用于存储缓存数据。不同SqlSession之间的缓存数据区域时互相不影响的。

一级缓存的作用域是同一个SqlSession,在同一个SqlSession中两次执行相同的sql语句,当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了,mybatis默认开启一级缓存。

 3.1 实例1:同一个SqlSession中两次查询id=1的记录

public class App {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User u1 = mapper.selectUserById(1);
            System.out.println(u1.getUser_name());
            // 利用缓存
            User u2 = mapper.selectUserById(1); 
            System.out.println(u2.getUser_name());
        } finally {
            session.close();
        }
    }
}  

截图:从日志可以看出,第二次相同sql语句的查询没有直接访问数据库,而是从缓存中获取。

3.2 实例2:同一个SqlSession中一次查询id=1,另一次查询id=2的记录

public class App {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            // id = 1
            User u1 = mapper.selectUserById(1);
            System.out.println(u1.getUser_name());
            // id = 2, 两次sql语句不同
            User u2 = mapper.selectUserById(2); 
            System.out.println(u2.getUser_name());
        } finally {
            session.close();
        }
    }
}  

结果截图:

3.3 实例3:同一个SqlSession中两次查询id=1的记录,但是中途清除一级缓存

public class App {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            // id = 1
            User u1 = mapper.selectUserById(1);
            System.out.println(u1.getUser_name());
            // 清除一级缓存
            session.clearCache();
            User u2 = mapper.selectUserById(1); 
            System.out.println(u2.getUser_name());
        } finally {
            session.close();
        }
    }
}  

截图:

4. 二级缓存

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同,即最终执行相同的sql语句,第一次执行完会将数据库中查询的数据写到缓存,第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。mybatis默认没有开启二级缓存需要在settings全局参数中配置开启二级缓存。

4.1 使用二级缓存

4.1.1 实体类User实现接口Serializable,否则报错

Exception in thread "main" org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: org.mybatis.mapper.User

4.1.2 mybatis-config.xml增加配置

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

必须开启缓存配置(默认是开启的),才能使用二级缓存。

4.1.3 UserMapper.xml开启缓存

<mapper namespace="org.mybatis.mapper.UserMapper">
   <cache/>
  <select id="selectUserById" resultType="org.mybatis.mapper.User">
    select * from user where id = #{id}
  </select>
  
   <select id="selectUserByName" parameterType="java.lang.String" resultType="org.mybatis.mapper.User">
    select * from user where user_name = #{name};
  </select>
</mapper>  

增加配置<cache />

4.1.4 App.java和结果

public class App {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session1 = sqlSessionFactory.openSession();
        SqlSession session2 = sqlSessionFactory.openSession();
        try {
            UserMapper mapper1 = session1.getMapper(UserMapper.class);
            UserMapper mapper2 = session2.getMapper(UserMapper.class);
            // id = 1
            User u1 = mapper1.selectUserById(1);
            System.out.println(u1.getUser_name());
            // 第一个session必须提交才能使用二级缓存。
            session1.commit();
            User u2 = mapper2.selectUserById(1); 
            System.out.println(u2.getUser_name());
            
        } finally {
            session1.close();
            session2.close();
        }
    }
}  

截图:

5. 总结

 mybatis一级缓存:默认开启

(1)必须同一个SqlSession,如果SqlSession对象已经close,就不能用了;

(2)查询条件必须一致;

(3)没有执行过session.cleanCache()

(4)没有执行过增删改操作(这些操作都会清理缓存)

mybatis二级缓存:

(1)mybatis-config.xml中默认配置

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

(2)手动在Mapper.xml中添加<cache>

<cache/> 有默认的参数值,比如

<cache

eviction="FIFO"  // 回收策略

flushInterval="60000" // 自动刷新时间60秒

size="512" // 最多缓存512个引用对象

readOnly="true" // 只读

/>  

(3)映射语句文件中的所有select语句将会被缓存

(4)映射语句文件中的所有insert,update和delete语句会刷新缓存

(5)缓存会使用Least Recently Used(LRU,最近最少使用)算法来收回

(6)缓存会根据指定的时间间隔来刷新

(7)缓存会默认存储1024个对象

x. 参考资料

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

https://www.cnblogs.com/little-fly/p/6251451.html

posted @ 2018-03-23 17:46  小路不懂2  阅读(214)  评论(0编辑  收藏  举报