一级缓存

import com.study.mybatis.config.SpringConfig;
import com.study.mybatis.entity.Country;
import com.study.mybatis.mapper.CountryMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author Zhang B H
 * @create 2023-10-04 15:40
 */
@ActiveProfiles("qa")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class CacheTest {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 测试 MyBatis 的一级缓存作用机制
     */
    @Test
    public void testL1Cache() {
        Country country1 = null;
        // 获取 sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);
            // 获取id为1的国家
            country1 = countryMapper.selectByPrimaryKey(1);
            // 修改获取到的对象的name实例字段
            country1.setName("NEW NAME");

            // 再次获取id相同的国家
            // 因为 一级缓存 的原因,因此此行代码并不会执行sql查询
            Country country2 = countryMapper.selectByPrimaryKey(1);
            // 此时,命中一级缓存
            Assert.assertEquals(country1, country2);
        } finally {
            sqlSession.close();
        }


        sqlSession = sqlSessionFactory.openSession();
        try {
            CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);
            Country country2 = countryMapper.selectByPrimaryKey(1);

            Assert.assertNotEquals("NEW NAME", country2.getName());
            Assert.assertNotEquals(country1, country2);

            // 执行删除操作
            // 此时,会清除一级缓存
            countryMapper.deleteByPrimaryKey(100);

            Country country3 = countryMapper.selectByPrimaryKey(1);
            Assert.assertNotEquals(country3, country2);
        } finally {
            sqlSession.close();
        }
    }
}

在第一次执行 selectByPrimaryKey 方法获取 Country 时,真正执行了数据库查询,得到了 country1 的结果。而在第二次执行获取 country2 的时候,并没有执行数据库操作。

从测试代码来看,获取 country1 后重新设置了 name 的值,之后没有进行任何更新数据库的操作。在获取 country2 后,可以发现,原来 country1 和 country2 竟然是同一个对象,之所以这样就是因为 MyBatis 的一级缓存

MyBatis 的一级缓存存在于 SqlSession 的生命周期中,在同一个 SqlSession 中查询时,MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个 Map 对象中。如果同一个 SqlSession 中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当 Map 缓存对象中已经存在该键值时,则会返回缓存中的对象。

在关闭第一个 SqlSession 后,又重新获取了一个 SqlSession,然后重新获取了 country2。country2 是一个新的实例,和 country1 没有任何关系。这是因为一级缓存是和 SqlSession 绑定的,只存在于 SqlSession 的生命周期中

接下来执行了一个 deleteByPrimaryKey 操作,然后使用相同的方法和参数获取了 country3。country3 和 country2 是完全不同的两个对象。这是因为任何的 INSERT、UPDATE、DELETE 操作都会清空一级缓存,所以查询 country3 的时候由于缓存不存在,就会再次执行数据库查询获取数据。

Last updated