mybatis缓存机制

目录

一、什么是缓存?为什么用缓存?

二、Mybatis的缓存机制

三、一级缓存

如何清除一级缓存?

使用一级缓存需要注意什么?

四、二级缓存

总结


一、什么是缓存?为什么用缓存?

缓存是保存在内存中的一块区域。使用缓存可以避免与数据库的直接交互,通过缓存将数据返回,提升查询效率的同时减少对数据库的压力。

二、Mybatis的缓存机制

Mybatis缓存分为一级缓存和二级缓存。

当查询的Sql语句相同,传递的参数值相同,对结果集的要求相同,预编译的模板Id相同

四个条件同时满足将触发缓存。

三、一级缓存

Mybatis默认开启一级缓存(不可关闭),在同一个sqlSession对象内共享缓存,缓存随着sqlSession的销毁而消失。

@Test
public void test() throws IOException {
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session = factory.openSession();


  // 使用同一个SqlSession查询
  UserMapper mapper1 = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session.getMapper(UserMapper.class);


  User user1 = mapper1.findById(1);
  System.out.println(user1.hashCode());
  System.out.println("-------------------------------------------");
  User user2 = mapper2.findById(1);
  System.out.println(user2.hashCode());
}


@Test
public void test1() throws IOException {
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session1 = factory.openSession();
  SqlSession session2 = factory.openSession();


  // 使用不同的SqlSession查询
  UserMapper mapper1 = session1.getMapper(UserMapper.class);
  UserMapper mapper2 = session2.getMapper(UserMapper.class);


  User user1 = mapper1.findById(1);
  System.out.println(user1.hashCode());
  System.out.println("-------------------------------------------");
  User user2 = mapper2.findById(1);
  System.out.println(user2.hashCode());
}

第一个方法中两个对象的hashCode值相同,因为第二次查询是查询缓存,所以是同一个对象。

而第二个方法两个对象hashCode不同,因为查询时使用了不同的会话对象,缓存不共享。

如何清除一级缓存?

  1. SqlSession调用close():操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  2. SqlSession调用clearCache()/commit():操作会清空一级缓存数据。
  3. SqlSession调用增删改方法:增删改后数据库发生改变,缓存数据不准确,会清除缓存。

使用一级缓存需要注意什么?

  • 多线程环境下可能会读取到旧数据
    SqlSession session = sqlSessionFactory.openSession();
    User user1 = session.selectOne("getUserById", 1);  // 首次查询(缓存数据)
    
    // 其他线程或服务修改了数据库中的用户数据(但当前会话不知情)
    
    User user2 = session.selectOne("getUserById", 1);  // 仍返回旧数据(脏读)

    解决:对要求数据一致性较强的数据,比如说查询账户余额前先调用一次sqlSession.clearCache()清除缓存。

  • 如果查询结果集太大,会造成缓存占用大量内存。                                                                   解决:通过