TransactionalCache.java

  1. /*
  2.  *    Copyright 2009-2024 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.cache.decorators;

  17. import java.util.HashMap;
  18. import java.util.HashSet;
  19. import java.util.Map;
  20. import java.util.Set;

  21. import org.apache.ibatis.cache.Cache;
  22. import org.apache.ibatis.logging.Log;
  23. import org.apache.ibatis.logging.LogFactory;

  24. /**
  25.  * The 2nd level cache transactional buffer.
  26.  * <p>
  27.  * This class holds all cache entries that are to be added to the 2nd level cache during a Session. Entries are sent to
  28.  * the cache when commit is called or discarded if the Session is rolled back. Blocking cache support has been added.
  29.  * Therefore any get() that returns a cache miss will be followed by a put() so any lock associated with the key can be
  30.  * released.
  31.  *
  32.  * @author Clinton Begin
  33.  * @author Eduardo Macarron
  34.  */
  35. public class TransactionalCache implements Cache {

  36.   private static final Log log = LogFactory.getLog(TransactionalCache.class);

  37.   private final Cache delegate;
  38.   private boolean clearOnCommit;
  39.   private final Map<Object, Object> entriesToAddOnCommit;
  40.   private final Set<Object> entriesMissedInCache;

  41.   public TransactionalCache(Cache delegate) {
  42.     this.delegate = delegate;
  43.     this.clearOnCommit = false;
  44.     this.entriesToAddOnCommit = new HashMap<>();
  45.     this.entriesMissedInCache = new HashSet<>();
  46.   }

  47.   @Override
  48.   public String getId() {
  49.     return delegate.getId();
  50.   }

  51.   @Override
  52.   public int getSize() {
  53.     return delegate.getSize();
  54.   }

  55.   @Override
  56.   public Object getObject(Object key) {
  57.     // issue #116
  58.     Object object = delegate.getObject(key);
  59.     if (object == null) {
  60.       entriesMissedInCache.add(key);
  61.     }
  62.     // issue #146
  63.     if (clearOnCommit) {
  64.       return null;
  65.     }
  66.     return object;
  67.   }

  68.   @Override
  69.   public void putObject(Object key, Object object) {
  70.     entriesToAddOnCommit.put(key, object);
  71.   }

  72.   @Override
  73.   public Object removeObject(Object key) {
  74.     return null;
  75.   }

  76.   @Override
  77.   public void clear() {
  78.     clearOnCommit = true;
  79.     entriesToAddOnCommit.clear();
  80.   }

  81.   public void commit() {
  82.     if (clearOnCommit) {
  83.       delegate.clear();
  84.     }
  85.     flushPendingEntries();
  86.     reset();
  87.   }

  88.   public void rollback() {
  89.     unlockMissedEntries();
  90.     reset();
  91.   }

  92.   private void reset() {
  93.     clearOnCommit = false;
  94.     entriesToAddOnCommit.clear();
  95.     entriesMissedInCache.clear();
  96.   }

  97.   private void flushPendingEntries() {
  98.     for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
  99.       delegate.putObject(entry.getKey(), entry.getValue());
  100.     }
  101.     for (Object entry : entriesMissedInCache) {
  102.       if (!entriesToAddOnCommit.containsKey(entry)) {
  103.         delegate.putObject(entry, null);
  104.       }
  105.     }
  106.   }

  107.   private void unlockMissedEntries() {
  108.     for (Object entry : entriesMissedInCache) {
  109.       try {
  110.         delegate.removeObject(entry);
  111.       } catch (Exception e) {
  112.         log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
  113.             + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
  114.       }
  115.     }
  116.   }

  117. }