Level 1 Cache

MyBatis is an excellent persistence layer framework that simplifies database operations by providing SQL mapping and object-relational mapping functionality. Among them, MyBatis’s caching mechanism is one of its important performance optimization features, divided into level 1 cache and level 2 cache. Level 1 cache is enabled by default and is transparent to developers.

Level 1 cache is a local cache in MyBatis with a scope of SqlSession. For multiple identical query operations executed in the same SqlSession, if the parameters and SQL statements are the same, MyBatis returns query results directly from the cache without repeatedly accessing the database, thereby improving performance.

Code Testing

Test 1: Two Identical Queries in the Same SqlSession

public class WzkicuCache01 {

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<WzkUser> wzkUser = userMapper.findAll();
        System.out.println(wzkUser);
        List<WzkUser> wzkUser2 = userMapper.findAll();
        System.out.println(wzkUser2);
        sqlSession.close();
    }

}

Running Results

24/11/13 10:12:08 DEBUG UserMapper.findAll: ==>  Preparing: select *,o.id oid from wzk_user u left join wzk_orders o on u.id=o.uid;
24/11/13 10:12:08 DEBUG UserMapper.findAll: ==> Parameters:
24/11/13 10:12:08 DEBUG UserMapper.findAll: <==      Total: 3

Between the two queries, no SQL was executed, indicating that MyBatis level 1 cache was used.

Test 2: Query Then Perform Update Operation

public class WzkicuCache02 {

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // First query
        List<WzkUser> wzkUser = userMapper.findAll();
        System.out.println(wzkUser);
        // Perform an UPDATE
        WzkUser wzkUserUpdate = WzkUser
                .builder()
                .id(1)
                .username("wzk-update")
                .password("123-update")
                .build();
        wzkUserUpdate.setId(1);
        userMapper.updateById(wzkUserUpdate);
        sqlSession.commit();
        // Query again
        List<WzkUser> wzkUser2 = userMapper.findAll();
        System.out.println(wzkUser2);
        sqlSession.close();
    }

}

Running Results

The running result is: SQL query -> Update operation -> SQL query. After UPDATE, MyBatis clears the cache.

Working Principles

Level 1 cache is stored internally in SqlSession. It is implemented based on Java’s HashMap structure, using the SQL statement and parameters as keys, and query results as values.

Workflow

  1. Check Cache Before Query: SqlSession first checks whether corresponding data already exists in the cache. If the cache hits, the cached result is returned directly without executing the SQL statement.

  2. Update Cache After Query: If the cache misses, MyBatis executes the SQL query, obtains the result, and stores it in the level 1 cache.

  3. Cache Invalidation: Under certain circumstances, level 1 cache becomes invalid.

Characteristics of Level 1 Cache

  • Scope: Limited to the current SqlSession
  • Thread Safety: SqlSession is not thread-safe, so level 1 cache is only effective in a single thread
  • Lifecycle: Level 1 cache’s lifecycle is consistent with SqlSession; when SqlSession is closed or destroyed, the cache is cleared
  • Cache Granularity: Level 1 cache has fine granularity, limited to a single SqlSession

Conditions for Level 1 Cache to Take Effect

  • Identical SQL statements: The queried SQL and parameters must be exactly the same
  • Identical SqlSession: Query operations must be performed in the same SqlSession instance
  • No update operations performed: After executing INSERT, UPDATE, or DELETE operations, level 1 cache is cleared
  • Cache not explicitly cleared: If the clearCache() method is manually called, level 1 cache is cleared

Level 1 Cache Invalidation Scenarios

  • Different SqlSession: If the query occurs in different SqlSessions, level 1 cache becomes invalid
  • Performing Update Operations: When INSERT, UPDATE, or DELETE statements are executed, MyBatis clears the current SqlSession’s level 1 cache by default
  • Explicitly Clearing Cache: If clearCache() method of SqlSession is called in code, level 1 cache is cleared
  • Query Conditions Change: If the SQL or parameters of the query change, level 1 cache will not hit

Principle Exploration

What exactly is level 1 cache? When is level 1 cache created? What is the workflow of level 1 cache?

Through source code analysis, it can be found that:

  1. Cache-related code implementation can be viewed in BaseExecutor
  2. The clearCache method is related to caching
  3. PerpetualCache is essentially a large Map<Object, Object>
  4. When creating cache, it goes through a series of UPDATE methods, executed by CacheKey objects

Level 1 cache is a local cache implemented based on HashMap, enabled by default, with lifecycle consistent with SqlSession.