Mybatis一级缓存
mybatis的一级缓存是sqlsession级别的,默认开启
使一级缓存失效的四种情况:
1.不同sqlsession
2.同一个sqlsession 但是执行的语句不同 (先前已经缓存的sql中没有和现在要执行的sql一样的)
2.1 如果调用的方法名不同,但是执行的sql语句相同,仍然会使得一级缓存失效
3.进行了一次写操作,会清空一级缓存
4.手动清除了缓存 sqlSession.clearCache();
Mybatis二级缓存
开启二级缓存: 1.配置文件中添加: <setting name="cacheEnabled" value="true"/> 2.mapper映射文件中添加: <cache></cache> 需要使用二级缓存的mapper添加,因为二级缓存的存储范围是SqlSessionFactory级别的 如果所有mapper都开启二级缓存会造成不必要的资源浪费 3.实体类实现 Serializable 接口 4.session执行sql语句后调用session.close() 将该session在这期间执行过的sql存储到二级缓存中,即SqlSessionFactory中。
使二级缓存失效的情况:
1.进行数据库的写操作完成。(即commit)
开启二级缓存后,用Mybatis进行数据库查询的过程:
查二级缓存,查到直接返回结果,没查到则查询一级缓存,如果一级缓存也没有,则查询数据库。
调用sqlsession.close()后,一级缓存中的数据会写入二级缓存。
缓冲关联标签
涉及到 两个不同的mapper(前提是这两个mapper都由同一个SqlSessionFactory生成),一个emp 和 dept的多表查询(用empMapper中的多表查询sql) ,这时进行deptMapper的写操作,然后再进行一次多表查询
不会得到最新的结果,二级缓冲是 mapper级别,不同mapper 不会互相干扰和使用
这种情况下,会造成 “脏数据”。
解决方案:
在 查询的mapper中 添加一个 标签 缓冲关联标签
测试代码:
package com.example.mapper;
import com.example.entity.Brand;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class TestCache {
public final static String configName="SqlMapConfig.xml";
/*
mybatis的一级缓存是sqlsession级别的,默认开启
使一级缓存失效的四种情况:
1.不同sqlsession
2.同一个sqlsession 但是执行的语句不同 (先前已经缓存的sql中没有和现在要执行的sql一样的)
2.1 如果调用的方法名不同,但是执行的sql语句相同,仍然会使得一级缓存失效
3.进行了一次写操作
4.手动清除了缓存 sqlSession.clearCache();
*/
@Test
public void testCache1(){
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
//同一个sqlsession 不同mapper 执行一次sql语句 说明一级缓存至少是sqlsession级别的
brandMapper1.selectAll();
brandMapper2.selectAll();
}
@Test
public void testCache2(){
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sessionFactory.openSession(true);//开启自动提交
SqlSession sqlSession2 = sessionFactory.openSession(true);//开启自动提交
//不同session 执行了两次sql语句 结合testCache1 说明一级缓存是sqlsession级别的
BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
brandMapper1.selectAll();
brandMapper2.selectAll();
}
@Test
public void testCache3(){
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
//同一个sqlsession 不同mapper 但是中间有写操作
brandMapper1.selectAll();
Brand brand = new Brand();
brand.setBrandName("悦刻3");
brand.setCompanyName("尼古丁真");
brand.setOrdered(66);
brand.setDescription("电子烟假,尼古丁真");
brand.setStatus(1);
brandMapper1.addBrand(brand);
brandMapper2.selectAll();
}
@Test
public void testCache4(){
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
//同一个sqlsession 不同mapper 手动清除了缓存
brandMapper1.selectAll();
sqlSession.clearCache();//清除缓存
brandMapper2.selectAll();
//结果是执行了两次sql语句
}
@Test
public void testCache5(){
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.selectAll();
brandMapper.selectAll2();//即使两个方法中的sql语句相同,但是方法名不同,一级缓存失效,还是执行了两条sql
}
@Test
public void testCache6(){
/*
两个不同的session 其中session1执行两次查询所有操作,在第二次查询前,session插入一条数据
由于一级缓存的存在,session的第二次查询不会查询数据库,从而得到了脏数据。
开启二级缓存可以解决这个问题
*/
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sessionFactory.openSession(true);
SqlSession sqlSession2 = sessionFactory.openSession(true);
BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
List<Brand> brands1 = brandMapper1.selectAll();
System.out.println(brands1.size());
Brand brand = new Brand();
brand.setBrandName("悦刻3");
brand.setCompanyName("尼古丁真");
brand.setOrdered(66);
brand.setDescription("电子烟假,尼古丁真");
brand.setStatus(1);
brandMapper2.addBrand(brand);
brandMapper2.selectAll();//session2执行了查询所有语句
List<Brand> brands2 = brandMapper1.selectAll();
System.out.println(brands2.size());
}
@Test
public void testCache7(){
/*
开启二级缓存:
1.配置文件中添加: <setting name="cacheEnabled" value="true"/>
2.mapper映射文件中添加: <cache></cache>
需要使用二级缓存的mapper添加,因为二级缓存的存储范围是SqlSessionFactory级别的
如果所有mapper都开启二级缓存会造成不必要的资源浪费
3.实体类实现 Serializable 接口
4.session执行sql语句后调用session.close() 将该session在这期间执行过的sql存储到二级缓存中,即SqlSessionFactory中。
*/
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sessionFactory.openSession(true);
SqlSession sqlSession2 = sessionFactory.openSession(true);
BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
brandMapper1.selectAll();//0.0 0/1
brandMapper2.selectAll();//0.0 因为此时s1没有close 0/2
sqlSession2.close();
brandMapper1.selectAll();//0.33333 s2 close 保存了selectAll的结果在二级缓存 此时命中 Cache Hit Ratio= 1/3
sqlSession1.close();
}
@Test
public void testCache8(){
/*
修改表的操作发生后,二级缓存和一级缓存都会被清除
*/
InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sessionFactory.openSession(true);
SqlSession sqlSession2 = sessionFactory.openSession(true);
SqlSession sqlSession3 = sessionFactory.openSession(true);
BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
BrandMapper brandMapper3 = sqlSession3.getMapper(BrandMapper.class);
List<Brand> brands1 = brandMapper1.selectAll(); // 0.0 0/1
System.out.println(brands1.size());
// brandMapper2.selectAll();//session2执行了查询所有语句
Brand brand = new Brand();
brand.setBrandName("悦刻3");
brand.setCompanyName("尼古丁真");
brand.setOrdered(66);
brand.setDescription("电子烟假,尼古丁真");
brand.setStatus(1);
brandMapper2.addBrand(brand); //在commit动作发生后,二级缓存和一级缓存被清空
//二级缓存和一级缓存被清空,被清空后,甚至连访问二级缓存这个操作都不会发生,直接访问数据库,所以这里不是 0/2
brandMapper2.selectAll();
sqlSession2.close();
/*
Cache Hit Ratio [com.example.mapper.BrandMapper]: 0.5
请注意 这里的Cache Hit Ratio是每次查询累加的命中率,并不是单次查询的命中率,也肯定不是这个。
*/
brandMapper3.selectAll();//0.5 1/2
brandMapper3.selectAll();//0.6666666666666666 2/3
brandMapper3.selectAll();//0.75 3/4
brandMapper3.selectAll();//0.8 4/5
brandMapper3.selectAll();//0.8333333333333334 5/6
sqlSession3.close();
}
}