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中的各种操作充满未知和风险。
- Mybatis Cache 源码
- mybatis 源码系列 组件之 cache
- Mybatis源码学习(三)cache包
- mybatis cache
- MyBatis的缓存(Cache)
- mybatis 自定义缓存 cache
- 深入浅出Mybatis-改造Cache
- 深入浅出Mybatis-改造Cache
- Mybatis Cache探究
- 深入浅出Mybatis-改造Cache
- 深入浅出Mybatis-改造Cache
- 深入浅出Mybatis-改造Cache
- MyBatis L2 Cache ---ignite
- Mybatis Cache探究
- mybatis cache 设置
- MyBatis缓存(Cache)
- shiftone-cache 源码剖析
- leveldb源码分析:Cache
- JavaEE程序猿之hello的真相 ⑧
- TortoiseSVN Clearup failed to process the following paths * is already locked.解决方法
- 字体设计
- 查看电脑连接过的所有wifi密码
- 【HDU】-1102-Constructing Roads(最小生成树)
- Mybatis Cache 源码
- SVN中与资源库同步时报告了错误。1 中的 0 个资源已经同步
- Dynamic项目常用的分层结构和框架规范
- Android Fragment异常记录
- java native关键字
- android如何更改状态栏颜色
- tomcat 中文乱码
- [Leetcode,python] Reverse String 反转字符串
- Android之MVC设计模式