MyBatis Caching: First-Level, Second-Level, and EHCache Integration

Understanding MyBatis Caching Mechanisms

MyBatis incorporates an internal caching system designed to enhance application performance by minimizing redundant database interactions. This caching operates at two distinct levels: a local, session-scoped cache and a global, application-scoped cache.

First-Level Cache: SqlSession Scope

The first-level cache in MyBatis is inherently tied to the SqlSession instance. When data is fetched using a particular SqlSession, it is stored within that session's local cache. Subsequent requests for the identical data through the same SqlSession will retrieve results directly from this cache, bypassing database access. This mechanism is primarily effective for read (query) operations.

Conditions for First-Level Cache Invalidation

The first-level cache can be invalidated under several circumstances:

  • Different SqlSession Instances: Each new SqlSession object maintains its own independent first-level cache.
  • Varying Query Parameters: If the same query method is invoked with different parameters within the same SqlSession, the cache will not be hit.
  • Data Manipulation Operations: Any Data Manipulation Language (DML) operation (INSERT, UPDATE, DELETE) executed through the SqlSession will clear its first-level cache to ensure data consistency.
  • Explicit Cache Clearing: Developers can manually clear the first-level cache of a SqlSession using its clearCache() method.
sqlSession.clearCache(); // Manually clears the first-level cache

Second-Level Cache: SqlSessionFactory Scope

The second-level cache operates at the SqlSessionFactory level, making it global across multiple SqlSession instances created from that factory. Query results cached here are accessible by any SqlSession, providing an application-wide caching layer.

Activation Requirements for Second-Level Cache

To enable and utilize the second-level cache, the following conditions must be met:

  1. Global Configuration: The cacheEnabled property in the MyBatis core configuration file (mybatis-config.xml) must be set to true. This is its default value, so explicit configuration is often unnecessary.
  2. Mapper XML Configuration: The <cache /> tag must be present within the specific mapper XML file (e.g., UserMapper.xml) where caching is desired.
  3. SqlSession Lifecycle: The cached data becomes available to the second-level cache only after the SqlSession that performed the query is either committed or closed.
  4. Serializable Entities: All entity classes whose instances are stored in the second-level cache must implement the java.io.Serializable interface. This is crucial for object serialization and deserialization across different sessions and potential storage locations.

Second-Level Cache Invalidation

Similar to the first-level cache, any DML operation (INSERT, UPDATE, DELETE) performed through any SqlSession associated with the SqlSessionFactory will invalidate the affected portions of the second-level cache, ensuring data consistency.

Configuring the Second-Level Cache

The <cache /> tag in the mapper XML file supports several attributes for fine-tuning cache behavior:

  • eviction: Specifies the cache eviction policy.
    • LRU (Least Recent Used): Removes the object that has not been used for the longest time.
    • FIFO (First In, First Out): Evicts objects in the order they were added to the cache.
    • SOFT (Soft Reference): Objects are evicted based on the garbage collector's state and soft reference rules.
    • WEAK (Weak Reference): More aggressively evicts objects based on the garbage collector's state and weak reference rules.
  • flushInterval: Sets the cache refresh interval in milliseconds. If not set, the cache will only flush upon DML operations.
  • size: Defines the maximum number of objects the cache can store. Setting this too high can lead to excessive memory consumption.
  • readOnly: A boolean flag (true/false).
    • true: Returns the same instance of a cached object to all callers. This offers significant performance benefits but means objects cannot be modified after retrieval.
    • false: (Default) Returns a copy of the cached object (via serialization). This is slightly slower but guarantees thread safety as each caller receives its own modifiable instance.

MyBatis Cache Lookup Sequence

When MyBatis executes a query, it follows a specific order to check its caches:

  1. Second-Level Cache First: MyBatis first attempts to retrieve the data from the second-level cache. This is prioritized because it's a global cache, potentially holding data queried by other SqlSession instances.
  2. First-Level Cache Second: If the data is not found in the second-level cache, MyBatis then checks the current SqlSession's first-level cache.
  3. Database Access: If neither cache contains the requested data, MyBatis proceeds to execute the query against the database.

It's important to note that data stored in the first-level cache is promoted to the second-level cache when the associated SqlSession is closed or committed.

Enhancing Second-Level Cache with EHCache

While MyBatis provides a basic second-level cache, integrating external caching libraries like EHCache can offer more advenced features, performance, and persistence options. The first-level cache, being SqlSession-scoped, cannot be externally enhanced.

Adding Dependencies to pom.xml

To integrate EHCache with MyBatis, add the following dependencies to your project's pom.xml:

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- For a concrete SLF4J logging implementation -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

The roles of the key JAR files involved are:

JAR File Name Purpose
mybatis-ehcache Provides the integration bridge between MyBatis and EHCache.
ehcache The core EHCache library, handling cache management.
slf4j-api The logging facade API that EHCache (and many other libraries) uses.
logback-classic A concrete implementation of the SLF4J API for logging.

Creating ehcache.xml Configuration File

Configure EHCache settings by creating an ehcache.xml file in your classpath (e.g., src/main/resources). This file defines cache policies for EHCache.

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="classpath:ehcache.xsd">

    <!-- Specifies the disk store path for overflow or persistent caches -->
    <diskStore path="java.io.tmpdir/ehcache_data"/>

    <!-- Default cache settings that apply to all caches unless overridden -->
    <defaultCache
            maxElementsInMemory="1000"         <!-- Max objects in memory -->
            maxElementsOnDisk="10000000"       <!-- Max objects on disk -->
            eternal="false"                    <!-- Whether items are eternal (never expire) -->
            overflowToDisk="true"              <!-- Whether to overflow to disk if memory is full -->
            timeToIdleSeconds="120"            <!-- Seconds before an element is evicted if not accessed -->
            timeToLiveSeconds="120"            <!-- Seconds before an element is evicted after creation -->
            diskExpiryThreadIntervalSeconds="120" <!-- Interval for disk expiry thread -->
            memoryStoreEvictionPolicy="LRU">  <!-- Eviction policy for memory store -->
    </defaultCache>
</ehcache>

Configuring Mapper to Use EHCache

Within your MyBatis mapper XML file, modify the <cache /> tag to specify EHCache as the implementation for the second-level cache:

<!-- Use EHCache as the second-level cache implementation -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

Integrating Logback for Enhanced Logging

Since EHCache and MyBatis (when using SLF4J) rely on an SLF4J facade, a concrete logging implementation like Logback is required to view detailed logs. Create a logback.xml configuration file in your classpath (e.g., src/main/resources):

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">

    <!-- Console appender for logging to standard output -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- Defines the log message format: time, level, thread, logger, message, newline -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger{36}] %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Sets the global logging level. Available levels: TRACE, DEBUG, INFO, WARN, ERROR -->
    <root level="DEBUG">
        <!-- References the console appender defined above -->
        <appender-ref ref="CONSOLE" />
    </root>

    <!-- Specifies a DEBUG level for mappers within a specific package -->
    <logger name="com.yourcompany.mapper" level="DEBUG"/>
</configuration>

Tags: MyBatis Caching EHCache sqlsession SqlSessionFactory

Posted on Wed, 20 May 2026 16:32:32 +0000 by Jackount