MyBatis缓存

2020-07-15  本文已影响0人  Doooook

一级缓存

MyBatis对缓存提供支持,但是在没有配置的默认的情况下,它只开启一级缓存(一级缓存只是相对于同一个SqlSession而言)。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用同一个Mapper的方法,往往只执行一次SQL,因为使用SqlSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession都只会取出当前缓存的数据,而不会再次发送SQL到数据库。
但是如果你使用的是不同的SqlSesion对象,因为不同的SqlSession都是相互隔离的,所以即使用相同的Mapper、参数和方法,它还是会再次发送SQL到数据库去执行,返回结果。

SqlSessionFactoryUtil

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

/**
 * @author: Mitter
 * @date: 2020-06-21 21:13
 */
public class SqlSessionFactoryUtil {

    /**
     * SqlSessionFactory对象
     */
    private static SqlSessionFactory sqlSessionFactory = null;

    /**
     * 类线程锁
     */
    private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;

    /**
     * 私有化构造参数,不让实例化,只能通过类来访问
     * 避免使用者使用new的方式去创建多个对象
     */
    private SqlSessionFactoryUtil() {}

    /**
     * 构建SqlSessionFactory,单利模式,懒汉式
     * 某一个对象在应用中承担唯一职责的时候就是用单利模式,在本例中,SqlSessionFactory的唯一职责就是创建SqlSession
     */
    private static void initSqlSessionFactory() {
        String resource = "mybatis_config.xml";
        InputStream inputStream = null;

        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 静态方法使用类锁
        synchronized (CLASS_LOCK) {
            if (sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
        }
    }

    /**
     * 打开SqlSession
     * @return SqlSession
     */
    public static SqlSession openSqlSession() {
        if (sqlSessionFactory == null) {
            initSqlSessionFactory();
        }
        return sqlSessionFactory.openSession();
    }
}

测试观察一级缓存

观察日志发现第一个SqlSession实际只发生过一次查询,而第二次查询就从缓存中取出了,也就是SqlSession层面的一级缓存。

/**
 * @author: Doooook
 * @date: 2020-07-19 09:38
 */
public class MyBatisTest {

    public static void main(String[] args) {
        testMyBatisCache();
    }

    private static void testMyBatisCache() {
        SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper1 = sqlSession.getMapper(StudentMapper.class);
        // 使用同一个sqlSession再执行一次
        Student student1 = studentMapper1.selectByPrimaryKey(1);
        Student student2 = studentMapper1.selectByPrimaryKey(1);
        System.out.println("over!!!");
    }

}
image.png

二级缓存

为了客服一级缓存SqlSession间相互隔离的问题,我们往往需要配置二级缓存,使得缓存在SqlSessionFactory层面上能够提供给各个SqlSession对象共享。
而SqlSessionFactory层面上的二级缓存是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的,也就是要求实现Serializable接口,配置的方法很简单,只需要在映射XML文件配置就可以开启缓存了,在响应的方法上加上<cache/>标签即可。

  <cache/>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
    select 
    <include refid="Base_Column_List" />
    from t_student
    where id = #{id,jdbcType=INTEGER}
  </select>

这样的一个语句里面,很多设置是默认的,如果我们只是这样配置,那么就意味着:

测试观察二级缓存

同一个SqlSessionFactory下的不同SqlSession使用了二级缓存,只发送了一次SQL查询

private static void testMyBatisSndCache() {
        SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        Student student = new Student();
        student.setCnname("陈六");
        student.setSex(Byte.valueOf("1"));
        student.setSelfcardNo(100034);
        student.setNote("");
        student.setStatus(Byte.valueOf("0"));
        // 执行了更新操作(delete、update、insert)后,缓存就会被清空,下面的查询就会再次发送SQL查询数据库
        int insert = studentMapper.insert(student);
        // 请注意,当使用二级缓存的时候,sqlSession调用了commit方法后才会生效
        // 只有commit以后才会清楚缓存
        sqlSession.commit();

        SqlSession sqlSession2 = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
        Student student2 = studentMapper2.selectByPrimaryKey(1);
        // 使用同一个SqlSessionFactory下的不同SqlSession
        SqlSession sqlSession3 = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);
        Student student22 = studentMapper2.selectByPrimaryKey(1);
        // 请注意,当使用二级缓存的时候,sqlSession调用了commit方法后才会生效
        sqlSession2.commit();
    }
image.png
上一篇下一篇

猜你喜欢

热点阅读