美文网首页
MyBatis缓存

MyBatis缓存

作者: Doooook | 来源:发表于2020-07-15 22:56 被阅读0次

    一级缓存

    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>
    

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

    • 映射语句文件中的所有select语句将会被缓存。
    • 映射语句文件中的所有insert、update和delete语句会刷新缓存。
    • 缓存会使用默认的LeastRecentlyUsed(LRU,最近最少使用的)算法来收回。
    • 根据时间表,比如NoFlushInterval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新。
    • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用。
    • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,不干扰其他调用者或线程所做的潜在修改。

    测试观察二级缓存

    同一个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

    相关文章

      网友评论

          本文标题:MyBatis缓存

          本文链接:https://www.haomeiwen.com/subject/ecjshktx.html