Mybatis Cache 源码

来源:互联网 发布:java后端需要学什么 编辑:程序博客网 时间:2024/04/28 15:40

Cache接口:

public interface Cache {  String getId();  void putObject(Object key, Object value);  Object getObject(Object key);  Object removeObject(Object key);  void clear();  int getSize();  ReadWriteLock getReadWriteLock();}

这里写图片描述

SynchronizedCache 实现类,同步缓存

public class SynchronizedCache implements Cache {  // 形成链式结构,增强功能,装饰者模式  private Cache delegate;  public SynchronizedCache(Cache delegate) {    this.delegate = delegate;  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public synchronized int getSize() {    return delegate.getSize();  }  @Override  public synchronized void putObject(Object key, Object object) {    // 在同步的前提下,实现从cache增加数据    delegate.putObject(key, object);  }  @Override  public synchronized Object getObject(Object key) {    return delegate.getObject(key);  }  @Override  public synchronized Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public synchronized void clear() {    delegate.clear();  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  @Override  public ReadWriteLock getReadWriteLock() {    return null;  }}

典型的就是:
SynchronizedCache(同步)->LoggingCache(日志)->SerializedCache(序列化)->LruCache(最近最少使用)->PerpetualCache(永久缓存)

LoggingCache :

public class LoggingCache implements Cache {  private Log log;  // 链式结构    private Cache delegate;  // 请求个数  protected int requests = 0;  // 命中个数  protected int hits = 0;  public LoggingCache(Cache delegate) {    this.delegate = delegate;    this.log = LogFactory.getLog(getId());  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  @Override  public void putObject(Object key, Object object) {    delegate.putObject(key, object);  }  @Override  public Object getObject(Object key) {    // 请求个数自增    requests++;    final Object value = delegate.getObject(key);    if (value != null) {      // 命中次数自增      hits++;    }    if (log.isDebugEnabled()) {      log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());    }    return value;  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();  }  @Override  public ReadWriteLock getReadWriteLock() {    return null;  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  // 计算命中率  private double getHitRatio() {    return (double) hits / (double) requests;  }}

SerializedCache :

public class SerializedCache implements Cache {  private Cache delegate;  public SerializedCache(Cache delegate) {    this.delegate = delegate;  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  @Override  public void putObject(Object key, Object object) {    if (object == null || object instanceof Serializable) {      // 序列化后再存储      delegate.putObject(key, serialize((Serializable) object));    } else {      // 抛异常,只能缓存可序列化对象      throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);    }  }  @Override  public Object getObject(Object key) {    Object object = delegate.getObject(key);    // 反序列化后再返回    return object == null ? null : deserialize((byte[]) object);  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();  }  @Override  public ReadWriteLock getReadWriteLock() {    return null;  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  private byte[] serialize(Serializable value) {    try {      ByteArrayOutputStream bos = new ByteArrayOutputStream();      ObjectOutputStream oos = new ObjectOutputStream(bos);      oos.writeObject(value);      oos.flush();      oos.close();      return bos.toByteArray();    } catch (Exception e) {      throw new CacheException("Error serializing object.  Cause: " + e, e);    }  }  private Serializable deserialize(byte[] value) {    Serializable result;    try {      ByteArrayInputStream bis = new ByteArrayInputStream(value);      ObjectInputStream ois = new CustomObjectInputStream(bis);      result = (Serializable) ois.readObject();      ois.close();    } catch (Exception e) {      throw new CacheException("Error deserializing object.  Cause: " + e, e);    }    return result;  }  public static class CustomObjectInputStream extends ObjectInputStream {    public CustomObjectInputStream(InputStream in) throws IOException {      super(in);    }    @Override    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {      return Resources.classForName(desc.getName());    }  }}
public class LruCache implements Cache {  private final Cache delegate;  // 存储直接使用LinkedHashMap  private Map<Object, Object> keyMap;  // 将要被淘汰的对象  private Object eldestKey;  public LruCache(Cache delegate) {    this.delegate = delegate;    // 初始化大小,负载因子为0.75    // accessOrder the ordering mode - <tt>true</tt> for access-order, <tt>false</tt> for insertion-order(使用LinkedHashMap维护顺序,以访问顺序为准)    setSize(1024);  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  public void setSize(final int size) {    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {      private static final long serialVersionUID = 4267176411845948333L;      // 每次插入节点后执行      @Override      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {        boolean tooBig = size() > size;        if (tooBig) {          eldestKey = eldest.getKey();        }        return tooBig;      }    };  }  @Override  public void putObject(Object key, Object value) {    delegate.putObject(key, value);    cycleKeyList(key);  }  @Override  public Object getObject(Object key) {    // 触发一次排序    keyMap.get(key); //touch    return delegate.getObject(key);  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();    keyMap.clear();  }  @Override  public ReadWriteLock getReadWriteLock() {    return null;  }  private void cycleKeyList(Object key) {    keyMap.put(key, key);    if (eldestKey != null) {      // 维护cache的大小      delegate.removeObject(eldestKey);      eldestKey = null;    }  }}

PerpetualCache :

public class PerpetualCache implements Cache {  private String id;  private Map<Object, Object> cache = new HashMap<Object, Object>();  public PerpetualCache(String id) {    this.id = id;  }  @Override  public String getId() {    return id;  }  @Override  public int getSize() {    return cache.size();  }  @Override  public void putObject(Object key, Object value) {    cache.put(key, value);  }  @Override  public Object getObject(Object key) {    return cache.get(key);  }  @Override  public Object removeObject(Object key) {    return cache.remove(key);  }  @Override  public void clear() {    cache.clear();  }  @Override  public ReadWriteLock getReadWriteLock() {    return null;  }  @Override  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;    }    Cache otherCache = (Cache) o;    return getId().equals(otherCache.getId());  }  @Override  public int hashCode() {    if (getId() == null) {      throw new CacheException("Cache instances require an ID.");    }    return getId().hashCode();  }}

Mybatis 缓存引起的问题:

使用示例:

 static {        // 读取配置文件        String resource = "mybatis-config.xml";        Reader reader = null;        try {            reader = Resources.getResourceAsReader(resource);        } catch (IOException e) {            System.out.println(e.getMessage());        }        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);    }// ========================================================================     static {        sqlSessionFactory = MyBatisUtil.getSqlSessionFactory();    }    @Test    public void testAdd() {        SqlSession sqlSession = sqlSessionFactory.openSession();        try {            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);            User user = new User("lisi", new Integer(25));            userMapper.insertUser(user);            sqlSession.commit();// 这里一定要提交,不然数据进不去数据库中        } finally {            sqlSession.close();        }    }
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      return build(parser.parse());    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {      ErrorContext.instance().reset();      try {        inputStream.close();      } catch (IOException e) {        // Intentionally ignore. Prefer previous error.      }    }  }  // 最终返回了一个sqlSessionFactory实例对象  public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config);  }

DefaultSqlSession 默认会话

public class DefaultSqlSession implements SqlSession {  ...  @Override  public <T> T selectOne(String statement) {    return this.<T>selectOne(statement, null);  }  @Override  public <T> T selectOne(String statement, Object parameter) {    // Popular vote was to return null on 0 results and throw exception on too many.    List<T> list = this.<T>selectList(statement, parameter);    if (list.size() == 1) {      return list.get(0);    } else if (list.size() > 1) {      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());    } else {      return null;    }  }  ...  @Override  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  ...  @Override  public int insert(String statement) {    return insert(statement, null);  }  @Override  public int insert(String statement, Object parameter) {    return update(statement, parameter);  }  @Override  public int update(String statement) {    return update(statement, null);  }  @Override  public int update(String statement, Object parameter) {    try {      dirty = true;      MappedStatement ms = configuration.getMappedStatement(statement);      return executor.update(ms, wrapCollection(parameter));    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public int delete(String statement) {    return update(statement, null);  }  @Override  public int delete(String statement, Object parameter) {    return update(statement, parameter);  }  ...  @Override  public void clearCache() {    executor.clearLocalCache();  }  ...}

Executor 接口:

public interface Executor {  ResultHandler NO_RESULT_HANDLER = null;  int update(MappedStatement ms, Object parameter) throws SQLException;  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;  List<BatchResult> flushStatements() throws SQLException;  void commit(boolean required) throws SQLException;  void rollback(boolean required) throws SQLException;  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);  boolean isCached(MappedStatement ms, CacheKey key);  void clearLocalCache();  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);  Transaction getTransaction();  void close(boolean forceRollback);  boolean isClosed();  void setExecutorWrapper(Executor executor);}

CachingExecutor 带缓存功能的Executor

public class CachingExecutor implements Executor {  // 也形成链式结构,BaseExecutor的其他子类执行数据库操作,而本类为了增强功能。  private Executor delegate;  private TransactionalCacheManager tcm = new TransactionalCacheManager();  ...  @Override  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)      throws SQLException {    // Mapper <--> Cache    Cache cache = ms.getCache();    // 判断是否有<cache>    if (cache != null) { // 二级缓存      flushCacheIfRequired(ms);      if (ms.isUseCache() && resultHandler == null) {        //确保方法没有Out类型的参数,mybatis不支持存储过程的缓存,所以如果是存储过程,这里就会报错。        ensureNoOutParams(ms, parameterObject, boundSql);        @SuppressWarnings("unchecked")        List<E> list = (List<E>) tcm.getObject(cache, key);        if (list == null) {          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);          // 写入缓存          tcm.putObject(cache, key, list); // issue #578 and #116        }        return list;      }    }    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  } ...}

二级缓存:

public class TransactionalCacheManager {  // 管理 Cache(MapperStatment或者namespace) <--> TransactionalCache  private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();  ...  public Object getObject(Cache cache, CacheKey key) {    // TransactionalCache.put()    return getTransactionalCache(cache).getObject(key);  }  public void putObject(Cache cache, CacheKey key, Object value) {    getTransactionalCache(cache).putObject(key, value);  }  ...  private TransactionalCache getTransactionalCache(Cache cache) {    TransactionalCache txCache = transactionalCaches.get(cache);    if (txCache == null) { //没有则新建      txCache = new TransactionalCache(cache);      transactionalCaches.put(cache, txCache);    }    return txCache;  }}

TransactionalCache是Cache的实现类Cache

public class TransactionalCache implements Cache {  private static final Log log = LogFactory.getLog(TransactionalCache.class);  private Cache delegate;  private boolean clearOnCommit;  // 已经提交待缓存的实体  private Map<Object, Object> entriesToAddOnCommit;  // 缓存未命中  private Set<Object> entriesMissedInCache;  ...  @Override  public Object getObject(Object key) {    // issue #116    Object object = delegate.getObject(key);    if (object == null) {      // 没有命中      entriesMissedInCache.add(key);    }    // issue #146    if (clearOnCommit) {      return null;    } else {      return object;    }  }  ...  @Override  public void putObject(Object key, Object object) {    // 放入Map,已提交的所有实体    entriesToAddOnCommit.put(key, object);  }  ...  // 放入缓存  private void flushPendingEntries() {    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {      // 然后到第一部分分析的Cache      delegate.putObject(entry.getKey(), entry.getValue());    }    for (Object entry : entriesMissedInCache) {      if (!entriesToAddOnCommit.containsKey(entry)) {        delegate.putObject(entry, null);      }    }  }}

一级缓存:

BaseExecutor:

@SuppressWarnings("unchecked")  @Override  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());    if (closed) {      throw new ExecutorException("Executor was closed.");    }    if (queryStack == 0 && ms.isFlushCacheRequired()) {      clearLocalCache();    }    List<E> list;    try {      queryStack++;      // 从缓存中读取      // localcache 是 PerpetualCache,永久缓存      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;      if (list != null) {        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);      } else {        // 缓存中不存在,请求数据库        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);      }    } finally {      queryStack--;    }    if (queryStack == 0) {      for (DeferredLoad deferredLoad : deferredLoads) {        deferredLoad.load();      }      // issue #601      deferredLoads.clear();      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {        // issue #482        clearLocalCache();      }    }    return list;  }

二级缓存只能在【只有单表操作】的表上使用缓存
不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
在可以保证查询远远大于insert,update,delete操作的情况下使用缓存
这一点不需要多说,所有人都应该清楚。记住,这一点需要保证在1的前提下才可以!
可能会有很多人不理解这里,二级缓存带来的好处远远比不上他所隐藏的危害。
缓存是以namespace为单位的,不同namespace下的操作互不影响。
insert,update,delete操作会清空所在namespace下的全部缓存。
通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。
为什么避免使用二级缓存
在符合【Cache使用时的注意事项】的要求时,并没有什么危害。
其他情况就会有很多危害了。
针对一个表的某些操作不在他独立的namespace下进行。
例如在UserMapper.xml中有大多数针对user表的操作。但是在一个XXXMapper.xml中,还有针对user单表的操作。
这会导致user在两个命名空间下的数据不一致。如果在UserMapper.xml中做了刷新缓存的操作,在XXXMapper.xml中缓存仍然有效,如果有针对user的单表查询,使用缓存的结果可能会不正确。
更危险的情况是在XXXMapper.xml做了insert,update,delete操作时,会导致UserMapper.xml中的各种操作充满未知和风险。

0 0
原创粉丝点击