Mybatis cache implementation

SqlSessionFactory initialization: http://donald-draper.iteye.com/admin/blogs/2331917
Mybatis loading and parsing Mapper (xml) files Lecture 1: http://donald-draper.iteye.com/blog/2333125
Mybatis loading and parsing Mapper (xml) file second lecture: http://donald-draper.iteye.com/blog/2333191
Mybatis parsing Mapper (class): http://donald-draper.iteye.com/blog/2333293
Mybatis's Environment parsing Detailed explanation: http://donald-draper.iteye.com/admin/blogs/2334133
Mybatis dynamic tag statement analysis (BoundSql): http://donald-draper.iteye.com/admin/blogs/2334135
Mybatis Parse files, update SQLsession, query, often mention Cache, let's take a look today
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache
{
    public abstract String getId();
    public abstract int getSize();
    public abstract void putObject(Object obj, Object obj1);
    public abstract Object getObject(Object obj);
    public abstract Object removeObject(Object obj);
    public abstract void clear();
    public abstract ReadWriteLock getReadWriteLock();
}

From the Cache interface, it can be seen that Cache has id, size, ReadWriteLock attributes, put, get, remove Obejct operations;
let's take a look at the default cache PerpetualCache of Mybatis
public class PerpetualCache
    implements Cache
{
    private String id;
    private Map cache;
    private ReadWriteLock readWriteLock;
    public PerpetualCache(String id)
    {
        cache = new HashMap();
        readWriteLock = new ReentrantReadWriteLock();
        this.id = id;
    }
    public String getId()
    {
        return id;
    }
    public int getSize()
    {
        return cache.size();
    }
    public void putObject(Object key, Object value)
    {
        cache.put(key, value);
    }
    public Object getObject(Object key)
    {
        return cache.get(key);
    }
    public Object removeObject(Object key)
    {
        return cache.remove(key);
    }
    public void clear()
    {
        cache.clear();
    }
    public ReadWriteLock getReadWriteLock()
    {
        return readWriteLock;
    }
    public boolean equals(Object o)
    {
        if(getId() == null)
            throw new CacheException("Cache instances require an ID.");
        if(this == o)
            return true;
        if(!(o instanceof Cache))
        {
            return false;
        } else
        {
            Cache otherCache = (Cache)o;
            return getId().equals(otherCache.getId());
        }
    }
    public int hashCode()
    {
        if(getId() == null)
            throw new CacheException("Cache instances require an ID.");
        else
            return getId().hashCode();
    }
}

From PerpetualCache, we can see that the implementation of PerpetualCache actually reuses Map corresponding operations through Map, put, get, remove, and clear operations.
Look at LruCache again
public class LruCache
    implements Cache
{
    private final Cache _flddelegate;//Cache proxy
    private Map keyMap;//key Map
    private Object eldestKey;//The oldest key
    public LruCache(Cache delegate)
    {
        _flddelegate = delegate;
        setSize(1024);
    }
    public String getId()
    {
        return _flddelegate.getId();
    }
    public int getSize()
    {
        return _flddelegate.getSize();
    }
    //set cache size
    public void setSize(final int size)
    {
        //Get the thread-safe LinkedHashMap collection
        keyMap = Collections.synchronizedMap(new LinkedHashMap(0.75F, true, size) {
            //Rewrite removeEldestEntry at the same time, when this method returns true, remove eldest
            protected boolean removeEldestEntry(java.util.Map.Entry eldest)
            {
                boolean tooBig = size() > size;
                if (tooBig)
		    //If the current size of the Map is larger than the initialized Map size, assign the oldest key to the eldestKey
                    eldestKey = eldest.getKey();
                return tooBig;
            }

            private static final long serialVersionUID = 4267176411845948333L;
            final int val$size;
            final LruCache this$0;

            
            {
                this$0 = LruCache.this;
                size = i;
                super(x0, x1, x2);
            }
        });
    }
    // put the KV pair into the cache
    public void putObject(Object key, Object value)
    {
        _flddelegate.putObject(key, value);
	// and put the key into the Key Map of LRUCache
        cycleKeyList(key);
    }
    public Object getObject(Object key)
    {
        keyMap.get(key);
        return _flddelegate.getObject(key);
    }
    public Object removeObject(Object key)
    {
        return _flddelegate.removeObject(key);
    }
    public void clear()
    {
        _flddelegate.clear();
	//When clearing the cache, clear the Key Map
        keyMap.clear();
    }
    public ReadWriteLock getReadWriteLock()
    {
        return _flddelegate.getReadWriteLock();
    }
    //If the Key Map has reached the load limit, remove the eldestKey from the cache
    private void cycleKeyList(Object key)
    {
        keyMap.put(key, key);
        if(eldestKey != null)
        {
            _flddelegate.removeObject(eldestKey);
            eldestKey = null;
        }
    }
}

Summary:
The implementation of Mybatis cache is realized through Map. The addition, acquisition, and removal of objects in the cache are all related to Map operations; in LRUCache, there is a cache proxy inside, and LRU is added, acquired, and removed. The object is related to the operation of the cache proxy. There are two elements in the LRU, which are keyMap and eldestKey. keyMap is the corresponding key in the cache
. Yes, when adding an object to the item Map, do you need to remove the eldest java.util.Map.Entry object? After adding an object to the KeyMap, the size is larger than the initialized capacity, then remove the corresponding key from the KeyMap, and assign the key to eldestKey, when we add an object to the LRUCache, add the key to the KeyMap at the same time, and penalize whether it exceeds the capacity check, if it exceeds, remove the corresponding object from the proxy cache.

//LinkedHashMap
/**
     * Returns <tt>true</tt> if this map should remove its eldest entry.
     * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
     * inserting a new entry into the map.  It provides the implementor
     * with the opportunity to remove the eldest entry each time a new one
     * is added.  This is useful if the map represents a cache: it allows
     * the map to reduce memory consumption by deleting stale entries.
     *
     * <p>Sample use: this override will allow the map to grow up to 100
     * entries and then delete the eldest entry each time a new entry is
     * added, maintaining a steady state of 100 entries.
     * <pre>
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() > MAX_ENTRIES;
     *     }
     * </pre>
     *
     * <p>This method typically does not modify the map in any way,
     * instead allowing the map to modify itself as directed by its
     * return value.  It <i>is</i> permitted for this method to modify
     * the map directly, but if it does so, it <i>must</i> return
     * <tt>false</tt> (indicating that the map should not attempt any
     * further modification).  The effects of returning <tt>true</tt>
     * after modifying the map from within this method are unspecified.
     *
     * <p>This implementation merely returns <tt>false</tt> (so that this
     * map acts like a normal map - the eldest element is never removed).
     *
     * @param    eldest The least recently inserted entry in the map, or if
     *           this is an access-ordered map, the least recently accessed
     *           entry.  This is the entry that will be removed it this
     *           method returns <tt>true</tt>.  If the map was empty prior
     *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
     *           in this invocation, this will be the entry that was just
     *           inserted; in other words, if the map contains a single
     *           entry, the eldest entry is also the newest.
     * @return   <tt>true</tt> if the eldest entry should be removed
     *           from the map; <tt>false</tt> if it should be retained.
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

//Collections
/**
     * Returns a synchronized (thread-safe) map backed by the specified
     * map.  In order to guarantee serial access, it is critical that
     * [b]all[/b] access to the backing map is accomplished
     * through the returned map.<p>
     *
     * It is imperative that the user manually synchronize on the returned
     * map when iterating over any of its collection views:
     * <pre>
     *  Map m = Collections.synchronizedMap(new HashMap());
     *      ...
     *  Set s = m.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not s!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * </pre>
     * Failure to follow this advice may result in non-deterministic behavior.
     *
     * <p>The returned map will be serializable if the specified map is
     * serializable.
     *
     * @param  m the map to be "wrapped" in a synchronized map.
     * @return a synchronized view of the specified map.
     */
    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326942936&siteId=291194637