View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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  
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import org.apache.ibatis.cache.Cache;
24  import org.apache.ibatis.logging.Log;
25  import org.apache.ibatis.logging.LogFactory;
26  
27  /**
28   * The 2nd level cache transactional buffer.
29   * <p>
30   * This class holds all cache entries that are to be added to the 2nd level cache during a Session.
31   * Entries are sent to the cache when commit is called or discarded if the Session is rolled back.
32   * Blocking cache support has been added. Therefore any get() that returns a cache miss
33   * will be followed by a put() so any lock associated with the key can be released.
34   *
35   * @author Clinton Begin
36   * @author Eduardo Macarron
37   */
38  public class TransactionalCache implements Cache {
39  
40    private static final Log log = LogFactory.getLog(TransactionalCache.class);
41  
42    private final Cache delegate;
43    private boolean clearOnCommit;
44    private final Map<Object, Object> entriesToAddOnCommit;
45    private final Set<Object> entriesMissedInCache;
46  
47    public TransactionalCache(Cache delegate) {
48      this.delegate = delegate;
49      this.clearOnCommit = false;
50      this.entriesToAddOnCommit = new HashMap<>();
51      this.entriesMissedInCache = new HashSet<>();
52    }
53  
54    @Override
55    public String getId() {
56      return delegate.getId();
57    }
58  
59    @Override
60    public int getSize() {
61      return delegate.getSize();
62    }
63  
64    @Override
65    public Object getObject(Object key) {
66      // issue #116
67      Object object = delegate.getObject(key);
68      if (object == null) {
69        entriesMissedInCache.add(key);
70      }
71      // issue #146
72      if (clearOnCommit) {
73        return null;
74      } else {
75        return object;
76      }
77    }
78  
79    @Override
80    public void putObject(Object key, Object object) {
81      entriesToAddOnCommit.put(key, object);
82    }
83  
84    @Override
85    public Object removeObject(Object key) {
86      return null;
87    }
88  
89    @Override
90    public void clear() {
91      clearOnCommit = true;
92      entriesToAddOnCommit.clear();
93    }
94  
95    public void commit() {
96      if (clearOnCommit) {
97        delegate.clear();
98      }
99      flushPendingEntries();
100     reset();
101   }
102 
103   public void rollback() {
104     unlockMissedEntries();
105     reset();
106   }
107 
108   private void reset() {
109     clearOnCommit = false;
110     entriesToAddOnCommit.clear();
111     entriesMissedInCache.clear();
112   }
113 
114   private void flushPendingEntries() {
115     for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
116       delegate.putObject(entry.getKey(), entry.getValue());
117     }
118     for (Object entry : entriesMissedInCache) {
119       if (!entriesToAddOnCommit.containsKey(entry)) {
120         delegate.putObject(entry, null);
121       }
122     }
123   }
124 
125   private void unlockMissedEntries() {
126     for (Object entry : entriesMissedInCache) {
127       try {
128         delegate.removeObject(entry);
129       } catch (Exception e) {
130         log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
131             + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
132       }
133     }
134   }
135 
136 }