cache

MyBatis includes a powerful transactional query caching feature which is very configurable and customizable. A lot of changes have been made in the MyBatis 3 cache implementation to make it both more powerful and far easier to configure.

By default, just local session caching is enabled that is used solely to cache data for the duration of a session. To enable a global second level of caching you simply need to add one line to your SQL Mapping file:

  1. <cache/>

Literally that’s it. The effect of this one simple statement is as follows:

  • All results from select statements in the mapped statement file will be cached.
  • All insert, update and delete statements in the mapped statement file will flush the cache.
  • The cache will use a Least Recently Used (LRU) algorithm for eviction.
  • The cache will not flush on any sort of time based schedule (i.e. no Flush Interval).
  • The cache will store 1024 references to lists or objects (whatever the query method returns).
  • The cache will be treated as a read/write cache, meaning objects retrieved are not shared and can be safely modified by the caller, without interfering with other potential modifications by other callers or threads.

NOTE The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.

All of these properties are modifiable through the attributes of the cache element. For example:

  1. <cache
  2. eviction="FIFO"
  3. flushInterval="60000"
  4. size="512"
  5. readOnly="true"/>

This more advanced configuration creates a FIFO cache that flushes once every 60 seconds, stores up to 512 references to result objects or lists, and objects returned are considered read-only, thus modifying them could cause conflicts between callers in different threads.

The available eviction policies available are:

  • LRU – Least Recently Used: Removes objects that haven’t been used for the longst period of time.
  • FIFO – First In First Out: Removes objects in the order that they entered the cache.
  • SOFT – Soft Reference: Removes objects based on the garbage collector state and the rules of Soft References.
  • WEAK – Weak Reference: More aggressively removes objects based on the garbage collector state and rules of Weak References.

The default is LRU.

The flushInterval can be set to any positive integer and should represent a reasonable amount of time specified in milliseconds. The default is not set, thus no flush interval is used and the cache is only flushed by calls to statements.

The size can be set to any positive integer, keep in mind the size of the objects your caching and the available memory resources of your environment. The default is 1024.

The readOnly attribute can be set to true or false. A read-only cache will return the same instance of the cached object to all callers. Thus such objects should not be modified. This offers a significant performance advantage though. A read-write cache will return a copy (via serialization) of the cached object. This is slower, but safer, and thus the default is false.

NOTE Second level cache is transactional. That means that it is updated when a SqlSession finishes with commit or when it finishes with rollback but no inserts/deletes/updates with flushCache=true where executed.

Using a Custom Cache

In addition to customizing the cache in these ways, you can also completely override the cache behavior by implementing your own cache, or creating an adapter to other 3rd party caching solutions.

  1. <cache type="com.domain.something.MyCustomCache"/>

This example demonstrates how to use a custom cache implementation. The class specified in the type attribute must implement the org.apache.ibatis.cache.Cache interface and provide a constructor that gets an String id as an argument. This interface is one of the more complex in the MyBatis framework, but simple given what it does.

  1. public interface Cache {
  2. String getId();
  3. int getSize();
  4. void putObject(Object key, Object value);
  5. Object getObject(Object key);
  6. boolean hasKey(Object key);
  7. Object removeObject(Object key);
  8. void clear();
  9. }

To configure your cache, simply add public JavaBeans properties to your Cache implementation, and pass properties via the cache Element, for example, the following would call a method called setCacheFile(String file) on your Cache implementation:

  1. <cache type="com.domain.something.MyCustomCache">
  2. <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
  3. </cache>

You can use JavaBeans properties of all simple types, MyBatis will do the conversion. And you can specify a placeholder(e.g. ${cache.file}) to replace value defined at configuration properties.

Since 3.4.2, the MyBatis has been supported to call an initialization method after it’s set all properties. If you want to use this feature, please implements the org.apache.ibatis.builder.InitializingObject interface on your custom cache class.

  1. public interface InitializingObject {
  2. void initialize() throws Exception;
  3. }

NOTE Settings of cache (like eviction strategy, read write..etc.) in section above are not applied when using Custom Cache.

It’s important to remember that a cache configuration and the cache instance are bound to the namespace of the SQL Map file. Thus, all statements in the same namespace as the cache are bound by it. Statements can modify how they interact with the cache, or exclude themselves completely by using two simple attributes on a statement-by-statement basis. By default, statements are configured like this:

  1. <select ... flushCache="false" useCache="true"/>
  2. <insert ... flushCache="true"/>
  3. <update ... flushCache="true"/>
  4. <delete ... flushCache="true"/>

Since that’s the default, you obviously should never explicitly configure a statement that way. Instead, only set the flushCache and useCache attributes if you want to change the default behavior. For example, in some cases you may want to exclude the results of a particular select statement from the cache, or you might want a select statement to flush the cache. Similarly, you may have some update statements that don’t need to flush the cache upon execution.

cache-ref

Recall from the previous section that only the cache for this particular namespace will be used or flushed for statements within the same namespace. There may come a time when you want to share the same cache configuration and instance between namespaces. In such cases you can reference another cache by using the cache-ref element.

  1. <cache-ref namespace="com.someone.application.data.SomeMapper"/>