Analysis of MyBatis First‑Level and Second‑Level Cache Mechanisms
The article thoroughly examines MyBatis’s first‑level (session) and second‑level (namespace) caches, detailing their configurations, internal workflows, source‑code implementations, experimental behaviors, common pitfalls such as stale or dirty data, and advises disabling built‑in caching in favor of dedicated solutions for production.
MyBatis is a widely used Java persistence framework. While its default cache configuration works for many scenarios, the cache mechanism has several shortcomings that can lead to dirty data and hidden risks.
This article examines MyBatis caching from both usage and source‑code perspectives, covering first‑level (session) cache and second‑level (namespace) cache, their configurations, workflows, and practical experiments.
Table of Contents
First‑level cache introduction and configuration
First‑level cache workflow and source‑code analysis
First‑level cache summary
Second‑level cache introduction and configuration
Second‑level cache source‑code analysis
Second‑level cache summary
Overall conclusion
First‑Level Cache
Introduction
When the same SQL statement is executed multiple times within a single SqlSession, MyBatis can serve the result from the first‑level cache, avoiding repeated database hits and improving performance.
Each SqlSession holds an Executor which contains a LocalCache. The cache lookup process is:
Generate a CacheKey from the statement ID, offset, limit, SQL text, and parameters.
Check LocalCache for the key.
If found, return the cached result; otherwise query the database and store the result in the cache.
Configuration
Enable the first‑level cache by setting <setting name="localCacheScope" value="SESSION"/> (default) or STATEMENT for a narrower scope.
<setting name="localCacheScope" value="SESSION"/>Experiments
Various JUnit tests demonstrate cache behavior:
Experiment 1 : Three consecutive getStudentById(1) calls within the same session hit the cache after the first query.
Experiment 2 : An insert operation clears the first‑level cache, causing the subsequent query to hit the database.
Experiment 3 : Two separate SqlSessions share no cache; an update in one session does not affect the cache of the other, demonstrating session‑level isolation.
Workflow & Source‑Code Analysis
The core classes involved are SqlSession, Executor, BaseExecutor, CacheKey, and PerpetualCache. Key points include:
CacheKey construction uses statement ID, offset, limit, SQL, and parameters.
CacheKey equality checks the ordered list of these components.
BaseExecutor’s query first attempts to retrieve from localCache; if missing, it queries the database and writes back to the cache.
Update/insert/delete operations invoke clearLocalCache(), invalidating the first‑level cache.
Second‑Level Cache
Introduction
The second‑level cache shares cached data across multiple SqlSessions within the same namespace. When enabled, the query order becomes: second‑level cache → first‑level cache → database.
Configuration
Enable globally with <setting name="cacheEnabled" value="true"/> and declare a cache in each mapper XML: <cache/> Optional attributes include type, eviction, flushInterval, size, readOnly, and blocking. A cache‑ref can make different mappers share the same cache.
Experiments
Experiment 1 : Without committing the first session, the second session does not see cached data.
Experiment 2 : After committing the first session, the second session retrieves the result from the second‑level cache.
Experiment 3 : An update in another session invalidates the cache for the affected namespace.
Experiment 4 : Multi‑table queries can cause stale data because the second‑level cache is namespace‑scoped and does not react to changes in other namespaces.
Experiment 5 : Using cache‑ref to share a cache between namespaces solves the stale‑data issue but widens the cache granularity.
Source‑Code Analysis
The second‑level cache is implemented by CachingExecutor, which decorates BaseExecutor. The decorator chain is:
SynchronizedCache → LoggingCache → SerializedCache → LruCache → PerpetualCache.
Key methods: Cache cache = ms.getCache(); – obtain the cache associated with the mapped statement. flushCacheIfRequired(ms); – clears the cache for statements that require it (e.g., insert/update/delete).
During a query, tcm.getObject(cache, key) attempts to read from the transactional cache; on miss, the delegate executor is invoked and the result is stored via tcm.putObject(cache, key, list).
Commit triggers tcm.commit(), which flushes pending entries to the underlying cache and clears it if necessary.
Summary
First‑level cache lives for the duration of a SqlSession and is essentially an unbounded HashMap.
Second‑level cache extends caching across SqlSessions at the namespace level, but its default local implementation is not suitable for distributed environments.
Both caches can produce dirty data in multi‑session or multi‑table scenarios; careful configuration (e.g., using STATEMENT scope or disabling cache in production) is recommended.
Full Conclusion
The article provides a comprehensive walkthrough of MyBatis caching, including configuration, practical experiments, and deep source‑code inspection. For production systems, it is advisable to disable MyBatis’s built‑in cache and rely on dedicated caching solutions (e.g., Redis, Memcached) when needed.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Meituan Technology Team
Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
