Understanding MyBatis First‑Level and Second‑Level Caching with Code Examples

This article explains the principles and practical usage of MyBatis first‑level and second‑level caches, describing how caching reduces database load, showing configuration steps, code demonstrations, flow diagrams, and important considerations such as cache invalidation and LRU eviction.

Top Architect
Top Architect
Top Architect
Understanding MyBatis First‑Level and Second‑Level Caching with Code Examples

Cache refers to temporary data stored in memory; because memory is limited, only frequently accessed or immutable data such as dictionaries, system parameters, and status codes are cached to improve response speed and reduce database pressure.

In a typical BS architecture, most operations are CRUD on the database. By caching query results, the server can serve repeated requests directly from memory, lowering database load and achieving faster responses.

MyBatis First‑Level Cache

MyBatis provides two types of cache. The first‑level cache is enabled by default and scoped to a single SqlSession. All queries executed within the same session are stored in this cache. If an insert, update, or delete statement occurs, the cache is cleared.

Reader reader = Resources.getResourceAsReader("config/configuration.xml");
// create SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// ...
sqlSession.clearCache();
sqlSession.close();

Enabling SQL logging in the MyBatis configuration allows us to see how many times the database is actually accessed:

<settings>
    <!-- Standard log factory implementation that prints SQL logs -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

Running the following demo shows two query calls but only one database hit, confirming that the first‑level cache is effective:

public static void main(String[] args) throws IOException {
    Reader reader = Resources.getResourceAsReader("config/configuration.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = builder.build(reader);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.selectByPrimaryKey("3rfrf34r34");
    User user1 = mapper.selectByPrimaryKey("3rfrf34r34");
    System.out.println("Two user objects equal: " + (user == user1));
    sqlSession.clearCache();
    sqlSession.close();
}

When a query‑modify‑query sequence is executed, the cache is cleared after the modification, so the second query hits the database again.

MyBatis Second‑Level Cache

The second‑level cache is global and can be shared across multiple SqlSession instances. It must be explicitly enabled.

Cache data is first placed in the first‑level cache; when the session is committed or closed, it is flushed to the second‑level cache.

When enabled, MyBatis checks the second‑level cache before the first‑level cache.

Configuration Method 1 – Per Mapper

Add a <cache/> element to the specific mapper XML file:

<!-- Enable second‑level cache for this mapper -->
<cache/>

Configuration Method 2 – Global

Enable caching for all mappers in the MyBatis global configuration:

<settings>
    <!-- Enable second‑level cache for all mappers -->
    <setting name="cacheEnabled" value="true"/>
</settings>

Example demonstrating two separate sessions sharing the same cached result:

public static void main(String[] args) throws IOException {
    Reader reader = Resources.getResourceAsReader("config/configuration.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = builder.build(reader);
    // Session 1
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user = mapper1.selectByPrimaryKey("3rfrf34r34");
    sqlSession1.clearCache();
    sqlSession1.close();
    // Session 2
    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper2.selectByPrimaryKey("3rfrf34r34");
    sqlSession2.clearCache();
    sqlSession2.close();
}

The log shows only one database query, confirming that the second‑level cache is working.

Important Considerations

All select statements in mapper files are cached.

Any insert, update, or delete statement will invalidate the cache.

The cache uses a Least‑Recently‑Used (LRU) eviction algorithm.

Cache does not refresh on a timed interval.

Up to 1024 object references are stored.

Cache is read/write; retrieved objects can be safely modified without affecting other callers.

Note: The original article also contains promotional material and QR‑code invitations, which are omitted here for brevity.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavacacheBackend DevelopmentMyBatisFirst-Level CacheSecond-Level Cache
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.