初识mybatis中的缓存

时间:2022-07-23
本文章向大家介绍初识mybatis中的缓存,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

mybatis中的缓存

一级缓存

mybati的一级缓存作用域为session,当执行opensession()后,结果和sql会被存入缓存中,如果下次执行的sql(参数 语句)相同就直接从缓存当中拿取,而不再执行sql从数据库查询

mybatis中一级缓存是默认开启的,并且是一直开启的且无法关闭

关闭掉当前的session对象可以达到强制清除缓存的效果

案例演示:

新建一个基本的MyBatis项目

Uesr类:

public class User {
    //account的实体封装属性
    private String name;
    private Integer id;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", id=" + id +
                ", money=" + money +
                '}';
    }

    private Float money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }
}

UserMapper类(暂时提供一个方法):

public interface UserMapper {
    /**
     * 根据id查询
     */
    public User findAllById(Integer id);
}

TestMybatis测试类:

@Test
public void findAllById() throws IOException {
    SqlSession session = MybatisUtil.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.findAllById(11);
    System.out.println(user);
}

注意这里的MybatisUtil类是单独的把session对象提取出来了

MybatisUtil类:

public class MybatisUtil {
    public static SqlSession openSession() throws IOException {
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
        return session;
    }
}

在测试类中测试一级缓存

不清除session

@Test
public void findAllById() throws IOException {
    SqlSession session = MybatisUtil.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    //第一次查询
    User user = mapper.findAllById(11);
    System.out.println(user);
    //进行第二次查询
    User user1 = mapper.findAllById(11);
    System.out.println(user1);
}

可以发现两次查询只使用了一次sql语句,使用同一个查询到的结果

控制台输出:

//sql语句
DEBUG [main] - ==>  Preparing: select * from account where id = ? 
DEBUG [main] - ==> Parameters: 11(Integer)
DEBUG [main] - <==      Total: 1
User{name='ccc', id=11, money=1000.0}
User{name='ccc', id=11, money=1000.0}

强制清除掉session

@Test
public void findAllById() throws IOException {
    SqlSession session = MybatisUtil.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    //第一次查询
    User user = mapper.findAllById(11);
    System.out.println(user);
    //清除缓存
    session.clearCache();
    System.out.println("清除缓存成功");
    //进行第二次查询
    User user1 = mapper.findAllById(11);
    System.out.println(user1);
}

控制台日志打印两次查询使用了两个sql语句

//第一次查询使用的sql
DEBUG [main] - ==>  Preparing: select * from account where id = ? 
DEBUG [main] - ==> Parameters: 11(Integer)
DEBUG [main] - <==      Total: 1
User{name='ccc', id=11, money=1000.0}
清除缓存成功
//第二次查询使用的sql
DEBUG [main] - ==>  Preparing: select * from account where id = ? 
DEBUG [main] - ==> Parameters: 11(Integer)
DEBUG [main] - <==      Total: 1
User{name='ccc', id=11, money=1000.0}

注意: 执行update、insert、delete的时候,会清空缓存;

当然这在逻辑中是绝对正确的,如果你在执行更新,添加 ,删除的时候还保存着上一次的数据和sql的话那么下一次就永远是上一次的结果,且只会是第一次的结果

在类中测试:

@Test
public void findAllById() throws IOException {
    SqlSession session = MybatisUtil.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    System.out.println(mapper.findAllById(15));
    User user = new User();
    user.setMoney(20000F);
    //执行update方法
    mapper.updateUser(user);
    //提交事务
    session.commit();
    //第二次查询
    System.out.println(mapper.findAllById(15));
}

通过前面的案例我们知道执行的sql相同只会执行一次sql而现在的控制台日志打印执行了两次sql说明执行的update方法清除掉了缓存

控制台日志打印:

//第一次查询
DEBUG [main] - ==>  Preparing: select * from account where id = ? 
DEBUG [main] - ==> Parameters: 15(Integer)
DEBUG [main] - <==      Total: 1
User{name='lk阿b', id=15, money=10000.0}
//执行update方法
DEBUG [main] - ==>  Preparing: update account SET money=? where id=? 
DEBUG [main] - ==> Parameters: 20000.0(Float), null
DEBUG [main] - <==    Updates: 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1c93084c]
 //第二次查询
DEBUG [main] - ==>  Preparing: select * from account where id = ? 
DEBUG [main] - ==> Parameters: 15(Integer)
DEBUG [main] - <==      Total: 1
User{name='lk阿b', id=15, money=10000.0}

二级缓存

mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

开启mybatis的二级缓存需要在mapper标签中添加标签以开启二级缓存

在UserMapper.xml配置文件中添加开启二级缓存的标签

<!--   开启二级缓存 -->
   <cache></cache>

在MybatisTest测试类中添加testCache()测试方法

@Test
public void testCache() throws IOException {
    //第一次查询
    SqlSession session = MybatisUtil.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    System.out.println(mapper.findAllById(15));
    //关闭session对话,相当于清除session的一级缓存
    session.close();
    //第二次
    SqlSession session1 = MybatisUtil.openSession();
    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    System.out.println(mapper1.findAllById(15));
}

运行testCache()方法会报java.io.NotSerializableException错误

原因是:二级缓存 必须要让缓存对象实现序列化接口;由于User类没有实现序列化接口,所以出现反序列异常;

那么我们让User类实现序列化接口Serializable就可以了;

public class User implements Serializable {
    //序列化id
    private static final long serialVersionUID = -7143822134269505369L;

关闭二级缓存可以在sqlMapConfig.xml中全局配置

<configuration>
    <properties resource="jdbc.properties"/>
    <settings>
<!--        开启二级缓存全局总开关,这里关闭了在userMapper中开启也没用-->
        <setting name="cacheEnabled" value="false"/>
    </settings>