# 一级缓存

```java
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 的一级缓存**。

{% hint style="info" %}
<mark style="color:blue;">**MyBatis 的一级缓存存在于 SqlSession 的生命周期中，在**</mark><mark style="color:blue;background-color:red;">**同一个 SqlSession**</mark>**&#x20;**<mark style="color:blue;">**中查询时，MyBatis 会把执行的方法和参数通过算法生成缓存的键值，将键值和查询结果存入一个 Map 对象中。**</mark>如果同一个 SqlSession 中执行的方法和参数完全一致，那么通过算法会生成相同的键值，当 Map 缓存对象中已经存在该键值时，则会返回缓存中的对象。
{% endhint %}

在关闭第一个 SqlSession 后，又重新获取了一个 SqlSession，然后重新获取了 country2。country2 是一个新的实例，和 country1 没有任何关系。这是因为<mark style="color:blue;">**一级缓存是和 SqlSession 绑定的，只存在于 SqlSession 的生命周期中**</mark><mark style="color:blue;">。</mark>

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

{% hint style="warning" %}
注入 mapper 对象，然后调用 mapper 上的方法来操作数据库，这种方式并不会触发一级缓存，这是因为每一次调用方法，都会获取一个新的 sqlSession。

```java
    @Autowired
    private CountryMapper countryMapper;

    @Test
    public void testL1Cache2() {

        // 获取id为1的国家
        Country country1 = countryMapper.selectByPrimaryKey(1);
        // 修改获取到的对象的name实例字段
        country1.setName("NEW NAME");

        // 再次获取id相同的国家
        Country country2 = countryMapper.selectByPrimaryKey(1);
        // 此时，并未命中一级缓存
        Assert.assertNotEquals(country1, country2);
    }
```

在上面的测试用例中，获取 country2 的方法仍然执行了 SQL 语句查询，因此，country1 和 country2 并不是同一个对象。
{% endhint %}
