Master MyBatis Caching in Spring Boot: Complete Guide to First‑ and Second‑Level Cache

This guide explains MyBatis's two‑level caching mechanism, shows how to use the default first‑level cache and manually enable the second‑level cache in a Spring Boot project, includes code examples, test cases, third‑party Ehcache integration, and key considerations for safe deployment.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
Master MyBatis Caching in Spring Boot: Complete Guide to First‑ and Second‑Level Cache

MyBatis Cache Mechanism Overview

MyBatis provides two‑level cache: first‑level (local) scoped to SqlSession, always enabled, cleared on flush/commit/close; second‑level (global) scoped to mapper namespace, disabled by default, shared across sessions, and written after session close/commit.

First‑Level Cache (Enabled by Default)

No extra configuration is required.

Example Mapper

@Mapper
public interface UserMapper {
    User selectUserById(Long id);
    void updateUserName(Long id, String name);
}

Test Cases

@SpringBootTest
public class MybatisCacheTest {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Test
    public void testFirstLevelCache() {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User u1 = mapper.selectUserById(1L); // hits DB
            User u2 = mapper.selectUserById(1L); // hits cache
            System.out.println(u1 == u2); // true
        }
    }

    @Test
    public void testFirstLevelCacheInvalidation() {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.selectUserById(1L);
            mapper.updateUserName(1L, "NewName"); // clears cache
            mapper.selectUserById(1L); // hits DB again
        }
    }
}

Key points: first‑level cache works only within the same SqlSession; any insert, update, or delete clears it to avoid stale data.

Second‑Level Cache (Manual Activation)

1. Enable in application.yml

mybatis:
  configuration:
    cache-enabled: true

2. Mapper Annotation

@Mapper
@CacheNamespace // enables second‑level cache
public interface UserMapper {
    User selectUserById(Long id);
}

3. XML Configuration Alternative

<mapper namespace="com.example.mapper.UserMapper">
    <cache/>
    <select id="selectUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

4. Entity Must Implement Serializable

@Data
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String name;
    private Integer age;
}

5. Verify Second‑Level Cache

@Test
public void testSecondLevelCache() {
    try (SqlSession s1 = sqlSessionFactory.openSession();
         SqlSession s2 = sqlSessionFactory.openSession()) {
        UserMapper m1 = s1.getMapper(UserMapper.class);
        UserMapper m2 = s2.getMapper(UserMapper.class);
        User u1 = m1.selectUserById(1L); // DB query
        s1.close(); // writes to second‑level cache
        User u2 = m2.selectUserById(1L); // hits cache
        System.out.println(u1 == u2); // false, but data equal
    }
}

Second‑level cache data is stored only after session commit/close; any write operation clears the cache for the affected mapper.

Using Third‑Party Cache (Ehcache Example)

1. Add Maven Dependencies

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.9.2</version>
</dependency>

2. Configure ehcache.xml

<ehcache updateCheck="false">
    <diskStore path="java.io.tmpdir/ehcache"/>
    <defaultCache maxElementsInMemory="10000" eternal="false"
                  timeToIdleSeconds="120" timeToLiveSeconds="120"
                  overflowToDisk="false"/>
    <cache name="com.example.mapper.UserMapper"
           maxElementsInMemory="1000" eternal="false"
           timeToIdleSeconds="300" timeToLiveSeconds="600"
           overflowToDisk="false"/>
</ehcache>

3. Update Mapper with Ehcache Implementation

@Mapper
@CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class)
public interface UserMapper {
    User selectUserById(Long id);
}

Important Considerations

Dirty‑data risk: cache may become inconsistent when multiple mappers modify the same table.

Transactional behavior: second‑level cache becomes visible only after commit or session close.

Suitable scenarios: read‑heavy, write‑light tables (e.g., dictionaries, configuration tables); avoid for write‑heavy business tables.

Distributed environments: combine second‑level cache with external solutions like Redis for scalability.

Cache Comparison

Scope : First‑level – SqlSession; Second‑level – Mapper namespace, shared across sessions.

Default state : First‑level enabled; Second‑level disabled.

Lifecycle : First‑level cleared with SqlSession; Second‑level lives with the application.

Invalidation : First‑level on any DML or commit/close; Second‑level on DML affecting the specific mapper.

Sharing : First‑level not shareable; Second‑level shareable.

Final Summary

First‑level cache is on by default, requires no configuration, and works only within a single SqlSession.

Second‑level cache must be explicitly enabled, is suitable for read‑dominant scenarios, and persists across sessions.

For production, third‑party caches such as Ehcache or Redis are recommended to improve scalability and performance.

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 BootMyBatisEhcacheFirst-Level CacheSecond-Level Cache
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow 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.