Mybatis缓存

文章目录

  • 前言
  • 一、Mybatis缓存机制
  • 二、一级缓存
  • 三、二级缓存
  • 四、Mybatis集成EhCache


前言

内存:临时存储数据的空间,断电后消失
硬盘:持久化的数据在硬盘当中。文件也存储在硬盘当中。关机然后重启硬盘中的文件还在。这是一个持久化的设备
缓存(cache):提前把数据放到内存中,下一次用的时候,直接从缓存中拿。效率高
实际上各大关系型数据库的数据,都是存储在文件当中
缓存通常是程序开发中优化程序的重要手段:字符串常量池、线程池、连接池、整数型常量池……


一、Mybatis缓存机制

执行DQL(Data Query Language)–select语句的时候,将查询到的结果存放在缓存(内存)当中,如果下一次还是执行同样的dql语句,直接从缓存中拿数据,不再从硬盘上找数据了。
目的:提高执行效率
缓存机制:使用减少IO的方式来提高效率
mybatis缓存包括:
一级缓存:将查询到的数据存储到SqlSession当中
二级缓存:将查询到的数据存储到SqlSessionFactory当中
或者集成其他第三方的缓存

补充
SqlSessionFactory的生命周期:一个数据库一个SqlSessionFactory
SqlSession的生命周期:当前的SQL会话

缓存只针对于DQL语句,也就是说缓存机制只对应select语句

二、一级缓存

一级缓存默认是开启的,不需要做任何配置。
原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存
代码如下: 根据id查找对应的汽车信息
接口:

public interface CarMapper {

    /**
     * 根据id获取Car信息
     * @param id
     * @return
     */
    Car selectById(Long id);
}

CarMapper.xml

<select id="selectById" resultType="car">
    select * from t_car
    where id=#{id}
select>

测试

 @Test
    public void testCcarMapper() throws IOException {
        SqlSession sqlsession  = SqlSessionUtil.openSession();
        CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
        Car car = carMapper.selectById(1L);
        System.out.println(car);
        sqlsession.close();
    }

查询正常。
在这里插入图片描述
现在进行缓存测试,在测试类中再进行一次查找返回查找信息为car1

    @Test
    public void testCcarMapper() throws IOException {
        SqlSession sqlsession  = SqlSessionUtil.openSession();
        CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
        Car car = carMapper.selectById(1L);
        System.out.println(car);
        Car car1 = carMapper.selectById(1L);
        System.out.println(car1);
        sqlsession.close();
    }

Mybatis缓存_第1张图片
根据日志信息提示只进行了一次SQL语句查询,是由于同一个mapper变量的原因?
再进行测试:创建一个变量carMapper2 ,让carMapper2 去执行第二条SQL语句

    @Test
    public void testCcarMapper() throws IOException {
        SqlSession sqlsession  = SqlSessionUtil.openSession();
        CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
        Car car = carMapper.selectById(1L);
        System.out.println(car);

        CarMapper carMapper2 = sqlsession.getMapper(CarMapper.class);
        Car car1 = carMapper2.selectById(1L);
        System.out.println(car1);
        sqlsession.close();
    }

两个carMapper 都是通过一个sqlsession对象获取的
依然还是只进行了一次SQL语句查询
Mybatis缓存_第2张图片
一级缓存存放在sqlSession中,那我们创建两个 sqlSession对象再去验证,
答案是可以的。但是,在之前工具类中,使用了ThreadLocal
SqlSessionUtil工具类:

package com.powernode.util;
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.IOException;

/**
 * Mybatis工具类
 * @author bwy
 */
public class SqlSessionUtil {
    static SqlSessionFactory factory = null;
    private SqlSessionUtil(){}
    static{
        try {
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //全局的服务器级别的 一个服务器当中定义一个即可,为了保证一个线程对应一个session
    //为什么把session对象放在ThreadLocal当中
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
    /**
     * 获取会话对象
     * @return
     * @throws IOException
     */
    public static SqlSession openSession() throws IOException {
         SqlSession session = local.get();
        if (session==null) {
            session = factory.openSession();
            //绑定 将session对象绑定到当前线程上
            local.set(session);
        }
        return session;
    }
    /**
     * 关闭session对象 (从当前线程中移除Session对象)
     * @param session
     */
    public static void close(SqlSession session){
        if (session!=null) {
            session.close();
            //注意移除session对象和当前线程的绑定关系
            //Tomcat服务器支持线程池, 也就是说用过的线程t1,可能下一次还会使用这个线程
            local.remove();
        }
    }
}

因此,如果要获取两个sqlSession对象测试,就不能再使用工具类
创建一个SqlSessionFactory可以创建多个SqlSession对象

   public void testCcarMapper2() throws IOException {
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession session1 = factory.openSession();
        CarMapper carMapper = session1.getMapper(CarMapper.class);
        Car car = carMapper.selectById(1L);
        System.out.println(car);

        SqlSession session2 = factory.openSession();
        CarMapper carMapper2 = session2.getMapper(CarMapper.class);
        Car car1 = carMapper2.selectById(1L);
        System.out.println(car1);

        session1.close();
        session2.close();
    }

Mybatis缓存_第3张图片
执行了两次SQL语句,获取了两个SqlSesion就是两个缓存。

思考
1、什么时候不走缓存?
SqlSession对象不是同一个,肯定不走缓存
查询条件不一样肯定也不走缓存
2、什么时候一级缓存失效?
第一次DQL和第二次DQL之间做了以下两件事中的任意一件,都会让一级缓存清空:
其一:执行了sqlSession的clearCache()方法,这是手动清空缓存。
其二:执行了insert或delete或update语句,不管是操作哪张表的,都会清空一级缓存
不做演示了。

三、二级缓存

二级缓存的范围是:SqlSessionFactory。
使用二级缓存需要具备以下几个条件:

1、< setting name=“cacheEnabled” value=“true”>全局性的开启或关闭所有映射器配置文件中已配置的任何缓存,默认就是true,无需设置
2、在使用二级缓存的SqlMapper.xml文件中添加配置:< cache/>
3、使用二级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口
4、SqlSession对象关闭或提交后,一级缓存中的数据才会被写入到二级缓存当中,此时二级缓存才可用

Mybatis缓存_第4张图片
在CarMapper.xml文件中添加标签

    
    <cache>cache>

实体类实现java.io.Serializable接口

public class Car implements Serializable{
	……
}

接口:

    /**
     * 根据id获取Car信息
     * 测试二级缓存
     * @param id
     * @return
     */
    Car selectById2(Long id);

CarMapper.xml

    
    <cache>cache>
<select id="selectById2" resultType="car">
    select * from t_car
    where id=#{id}
select>

测试:
此时测试类中将数据放入两个一级缓存,当session1 session2 执行close之后数据才会被放入二级缓存当中。

    @Test
    public void testCcarMapper3() throws IOException {
        //这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();
        CarMapper carMapper = session1.getMapper(CarMapper.class);
        CarMapper carMapper2 = session2.getMapper(CarMapper.class);

        //这行结束之后,实际上数据是缓存到一级缓存当中了(session1)
        Car car = carMapper.selectById2(1L);
        System.out.println(car);
        //如果这里不关闭SqlSession对象的话,二级缓存中是没有数据的。

        //这行结束之后,实际上数据是缓存到一级缓存当中了(session2) 两个一级缓存不一样
        Car car1 = carMapper2.selectById2(1L);
        System.out.println(car1);

        //程序执行到这里会将session1这个一级缓存中的数据写入到二级缓存当中
        session1.close();
        //程序执行到这里会将session2这个一级缓存中的数据写入到二级缓存当中
        session2.close();
    }

应该会调用二次SQL语句:
执行结果:
Mybatis缓存_第5张图片
调整一下session1关闭的位置。

    @Test
    public void testCcarMapper3() throws IOException {
        //这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();
        CarMapper carMapper = session1.getMapper(CarMapper.class);
        CarMapper carMapper2 = session2.getMapper(CarMapper.class);

        //这行结束之后,实际上数据是缓存到一级缓存当中了(session1)
        Car car = carMapper.selectById2(1L);
        System.out.println(car);
        //如果这里不关闭SqlSession对象的话,二级缓存中是没有数据的。
        //程序执行到这里会将session1这个一级缓存中的数据写入到二级缓存当中
        session1.close();

        //这行结束之后,实际上数据是缓存到一级缓存当中了(session2) 两个一级缓存不一样
        Car car1 = carMapper2.selectById2(1L);
        System.out.println(car1);
        
        //程序执行到这里会将session2这个一级缓存中的数据写入到二级缓存当中
        session2.close();
    }

执行结果:
session1结束之后,执行了close将数据放在二级缓存当中。因此session2执行时直接去二级缓存中拿数据,不会再执行SQL语句。
Mybatis缓存_第6张图片
只执行了一次SQL语句。

二级缓存什么时候失效?
只要两次查询之间出现了增删改操作,二级缓存就会失效【一级缓存也会失效】

二级缓存的一些设置
Mybatis缓存_第7张图片

四、Mybatis集成EhCache

集成EhCache是为了代替mybatis自带的二级缓存,一级缓存是无法替代的。


你可能感兴趣的:(mybatis,缓存,java)