一级缓存

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 的一级缓存

circle-info

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

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

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

circle-exclamation

Last updated