Unlock MyBatis Performance: A Deep Dive into First‑Level and Second‑Level Caching
This guide explains what caching is, why it’s essential for database‑driven applications, and how MyBatis implements first‑level and second‑level caches, including configuration steps, sample code, execution results, and key considerations to ensure effective cache usage.
What is a cache?
A cache stores a small amount of frequently accessed data in memory, allowing faster retrieval than querying the database each time. Because server memory is limited, only critical data—such as dictionaries, system parameters, or user session information—is cached.
Why use a cache?
In a typical BS architecture most operations are read‑heavy. Repeated database queries increase load and latency. By caching query results the application queries the database once, stores the result in memory, and serves subsequent requests directly from the cache, reducing database pressure and improving response time.
MyBatis first‑level (session) cache
MyBatis enables a first‑level cache by default. All queries executed within the same SqlSession are stored in this cache. The cache is cleared when an insert, update, or delete statement runs, or when the session is closed.
Reader reader = Resources.getResourceAsReader("config/configuration.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// ... perform queries ...
sqlSession.clearCache();
sqlSession.close();Enable SQL logging to verify cache usage:
<settings>
<!-- Standard log implementation that prints SQL -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>Demo 1 – Simple query reuse
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 user1 = mapper.selectByPrimaryKey("3rfrf34r34"); // first DB hit
User user2 = mapper.selectByPrimaryKey("3rfrf34r34"); // served from cache
System.out.println("Objects equal: " + (user1 == user2));
sqlSession.clearCache();
sqlSession.close();
}The console shows two SQL statements but only one actual database query, confirming that the second retrieval came from the first‑level cache.
Demo 2 – Query → Update → Query
public static void main(String[] args) throws IOException {
// setup omitted for brevity
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectByPrimaryKey("3rfrf34r34"); // DB hit
mapper.updateByPrimaryKey(user); // triggers cache clear
User userAgain = mapper.selectByPrimaryKey("3rfrf34r34"); // DB hit again
sqlSession.clearCache();
sqlSession.close();
}Three SQL statements appear because the update operation invalidates the cache, forcing the second query to hit the database.
MyBatis second‑level cache
The second‑level cache is global across sessions. It is populated when a session commits or closes, and subsequent sessions can read from it. Two configuration approaches exist:
Enable per‑mapper cache by adding <cache/> to the mapper XML.
Enable cache for all mappers via the global MyBatis setting cacheEnabled=true.
Per‑mapper configuration
<!-- Enable second‑level cache for this mapper -->
<cache/>Global configuration (mybatis-config.xml)
<settings>
<!-- Enable second‑level cache for all mappers -->
<setting name="cacheEnabled" value="true"/>
</settings>Second‑level cache demo
public static void main(String[] args) throws IOException {
Reader reader = Resources.getResourceAsReader("config/configuration.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(reader);
// First session
SqlSession session1 = sqlSessionFactory.openSession(true);
UserMapper mapper1 = session1.getMapper(UserMapper.class);
User u1 = mapper1.selectByPrimaryKey("3rfrf34r34"); // DB hit
session1.clearCache();
session1.close();
// Second session
SqlSession session2 = sqlSessionFactory.openSession(true);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User u2 = mapper2.selectByPrimaryKey("3rfrf34r34"); // Served from second‑level cache
session2.clearCache();
session2.close();
}The log shows only one database query, confirming that the second session retrieved the data from the second‑level cache.
Important considerations
All select statements are cached; any insert, update, or delete will invalidate the relevant cache entries.
The cache uses an LRU (Least Recently Used) eviction policy.
Cache entries are not refreshed on a timer; they persist until evicted or invalidated.
By default the cache holds up to 1024 object references.
Cached objects are treated as read/write – callers can modify them without affecting other threads.
Understanding these mechanisms and configuring MyBatis appropriately can significantly reduce database load and improve application responsiveness.
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.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.
