Mastering MyBatis Caching in Spring Boot: First‑Level and Second‑Level Explained

This article explains the purpose, differences, default settings, and practical verification of MyBatis first‑level and second‑level caches in Spring Boot, showing how cache scope, transaction boundaries, and mapper configuration affect cache behavior and performance.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
Mastering MyBatis Caching in Spring Boot: First‑Level and Second‑Level Explained

Spring Boot combined with MyBatis is now a standard stack, yet many developers overlook MyBatis's caching mechanisms, including both first‑level (session) and second‑level (mapper) caches. The author encountered a bug where optimistic‑lock retries always failed, traced it to the first‑level cache, and decided to document the cache concepts.

Why Caching Matters

In typical projects, caching reduces database load for rarely changing data. MyBatis's caches serve the same purpose: they lower DB pressure and improve system performance.

First‑Level Cache

First‑level cache is scoped to a single SqlSession. When a query for an entity (e.g., order with id 1) runs the first time, the result is stored in an in‑memory HashMap. Subsequent queries within the same session retrieve the data from this map, avoiding another DB hit. The cache is cleared automatically on commit (insert, update, delete) to prevent dirty reads. It is always enabled; its scope can be configured as SESSION or STATEMENT via localCacheScope in org.apache.ibatis.session.Configuration (default SESSION).

public Configuration() {
    // ...
    this.cacheEnabled = true; // second‑level cache switch
    this.localCacheScope = LocalCacheScope.SESSION; // first‑level cache scope
    // ...
}

Running a unit test that opens a session, obtains a mapper, and queries three times shows only the first query hits the database; the next two read from the first‑level cache. If each call obtains a new mapper from a fresh session (as in a typical Spring‑managed bean), the cache does not apply, and every call queries the DB.

Placing the calls inside a @Transactional method forces the same session to be reused, re‑enabling the first‑level cache and preventing the optimistic‑lock bug.

Second‑Level Cache

Second‑level cache is shared across multiple SqlSession instances but limited to a specific mapper namespace. It is stored in a global cache region (still a HashMap by default) and can be persisted to disk or remote stores. To enable it for a mapper, add a <cache/> element in the mapper XML and ensure the result POJOs implement java.io.Serializable.

<mapper namespace="com.secbro.mapper.OrderMapper">
    <cache/>
    <!-- other statements -->
</mapper>

Global activation is controlled by mybatis.configuration.cache-enabled=true (or the equivalent <setting name="cacheEnabled" value="true"/> in SqlMapConfig.xml). After enabling, a test that calls a service method three times within the same transaction shows cache hits, with the log indicating a hit probability of 0.5.

Disabling Cache for Specific Queries

If a particular select should bypass the second‑level cache, set useCache="false" on that <select> element.

<select id="findById" parameterType="int" resultMap="BaseResultMap" useCache="false">
    ...
</select>

Key Takeaways

First‑level cache works per SqlSession and is always on; it is cleared on commit.

Second‑level cache is optional, shared across sessions of the same mapper namespace, and requires POJOs to be serializable.

Transaction boundaries determine whether first‑level cache is effective; service‑level calls usually open a new session each time.

Enable/disable caches via configuration properties or mapper XML, and fine‑tune with flushInterval if needed.

Understanding these caching layers helps avoid subtle bugs (like stale data in optimistic‑lock retries) and improves performance for read‑heavy scenarios.

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.

cacheSpring BootMyBatisTransactionalFirst-Level CacheSecond-Level CacheSqlSession
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

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.