- 浏览: 13432 次
- 性别:
- 来自: 成都
最新评论
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
BaseExecutor#createCacheKey
@Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {[size=x-small][/size] if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // mimic DefaultParameterHandler logic for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; }
根据参数值,RowBounds,ParameterMapping确定缓存Key,每次update会更新Cachekey中的属性,equals的时候会比较各个字段的值,如果两次查询所传入的参数值,ParameterMapping都相等那么就是相同的cacheKey。
public void update(Object object) { int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); count++; checksum += baseHashCode; baseHashCode *= count; hashcode = multiplier * hashcode + baseHashCode; updateList.add(object); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof CacheKey)) { return false; } final CacheKey cacheKey = (CacheKey) object; if (hashcode != cacheKey.hashcode) { return false; } if (checksum != cacheKey.checksum) { return false; } if (count != cacheKey.count) { return false; } for (int i = 0; i < updateList.size(); i++) { Object thisObject = updateList.get(i); Object thatObject = cacheKey.updateList.get(i); if (!ArrayUtil.equals(thisObject, thatObject)) { return false; } } return true; }
createCacheKey方法在Excutor接口定义由BaseExecutor实现,Executor接口的继承接口如下:
[img]
http://my.iteye.com/admin/picture/138355
[/img]
除CachingExecutor之外直接实现Excutor,其他接口均是继承自BaseExcutor,而CachingExecutor只是对其他Executor进行包装:
public class CachingExecutor implements Executor { private final Executor delegate; //二级事务缓存 private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); }
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //此处获取的是SynchronizedCache的实例,在装饰的对象缓存方法加上Synchronized,二级缓存 Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); //只有对结果处理器为空的才使用二级缓存 //TODO为什么 if (ms.isUseCache() && resultHandler == null) { //parameterMapping只允许存在IN类型的参数映射 ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") //获取事务缓存(二级缓存//TODO) List<E> list = (List<E>) tcm.getObject(cache, key); //缓存为空则委托子类(SimpleExecutor查询),否则直接返回二级缓存结果 if (list == null) { //BaseExecutor#query 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); }
此处获取默认缓存是SynchronizedCache,原因是在XMLMapperBuilder#cacheElement解析<cache>节点构建缓存,调用BuilderAssistant#useNewCache,下面是构建缓存链的几个重要的流程:
public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { //缓存id为当前xml命名空间 Cache cache = new CacheBuilder(currentNamespace) //没指定实现默认使用PerpetualCache,内部是HashMap .implementation(valueOrDefault(typeClass, PerpetualCache.class)) //没指定过期装饰器默认使用最近最少使用LRUCache对PerpetualCache进行装饰 .addDecorator(valueOrDefault(evictionClass, LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build(); //添加到配置configuration configuration.addCache(cache); //设置当前缓存以便为MapperStatement使用 currentCache = cache; return cache; } public Cache build() { setDefaultImplementations(); //构造PerpetualCache Cache cache = newBaseCacheInstance(implementation, id); setCacheProperties(cache); // issue #352, do not apply decorators to custom caches if (PerpetualCache.class.equals(cache.getClass())) { //添加底层基础装饰器PerpetualCache为第一层,LruCache为第二层 for (Class<? extends Cache> decorator : decorators) { cache = newCacheDecoratorInstance(decorator, cache); //给LruCache设置相应属性,主要是size,默认1024 setCacheProperties(cache); } //设置装饰器链 cache = setStandardDecorators(cache); } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) { cache = new LoggingCache(cache); } return cache; } //设置默认实现和默认最下层的装饰器 private void setDefaultImplementations() { if (implementation == null) { implementation = PerpetualCache.class; if (decorators.isEmpty()) { decorators.add(LruCache.class); } } } private Cache setStandardDecorators(Cache cache) { try { //再次检查是否设置上 MetaObject metaCache = SystemMetaObject.forObject(cache); if (size != null && metaCache.hasSetter("size")) { metaCache.setValue("size", size); } if (clearInterval != null) { //添加过期feature cache = new ScheduledCache(cache); ((ScheduledCache) cache).setClearInterval(clearInterval); } if (readWrite) { //开启readWrite只能将实现了Serializable的对象添加到缓存 cache = new SerializedCache(cache); } //日志记录缓存命中率 cache = new LoggingCache(cache); //给缓存中add put size方法加上 SynchronizedCache cache = new SynchronizedCache(cache); if (blocking) { //blocking=true,是否阻塞 //每一个key都对应一把锁,当get的时候请求锁,延迟时间后未获取到锁抛出异常 cache = new BlockingCache(cache); } return cache; } catch (Exception e) { throw new CacheException("Error building standard cache decorators. Cause: " + e, e); } }
private void flushCacheIfRequired(MappedStatement ms) { //二级缓存 Cache cache = ms.getCache(); //MapperBuilderAssistant#292 // .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) //如果没有设置了flushCache ,则查询语句都是默认开启二级缓存,即不清除 if (cache != null && ms.isFlushCacheRequired()) { //flushCache=true或者是不是select //清除当前缓存 tcm.clear(cache); } }
单独解释下 tcm.clear(cache)
tcm是CachingExecutor的实例变量也是CachingExecutor被叫做CachingExecutor的原因,既然说到了缓存也说下CachingExecutor:
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } @Override public Transaction getTransaction() { return delegate.getTransaction(); } @Override public void close(boolean forceRollback) { try { //issues #499, #524 and #573 if (forceRollback) { tcm.rollback(); } else { tcm.commit(); } } finally { delegate.close(forceRollback); } } @Override public boolean isClosed() { return delegate.isClosed(); } @Override public int update(MappedStatement ms, Object parameterObject) throws SQLException { flushCacheIfRequired(ms); return delegate.update(ms, parameterObject); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } @Override public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException { flushCacheIfRequired(ms); return delegate.queryCursor(ms, parameter, rowBounds); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //二级缓存 Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, 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); } @Override public List<BatchResult> flushStatements() throws SQLException { return delegate.flushStatements(); } @Override public void commit(boolean required) throws SQLException { delegate.commit(required); tcm.commit(); } @Override public void rollback(boolean required) throws SQLException { try { delegate.rollback(required); } finally { if (required) { tcm.rollback(); } } } private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) { if (ms.getStatementType() == StatementType.CALLABLE) { for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) { if (parameterMapping.getMode() != ParameterMode.IN) { throw new ExecutorException("Caching stored procedures with OUT params is not supported. Please configure useCache=false in " + ms.getId() + " statement."); } } } } @Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql); } @Override public boolean isCached(MappedStatement ms, CacheKey key) { return delegate.isCached(ms, key); } @Override public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) { delegate.deferLoad(ms, resultObject, property, key, targetType); } @Override public void clearLocalCache() { delegate.clearLocalCache(); } //是否有必要清除缓存,select语句不进行清除,或者表明当前查询需要进行清除 private void flushCacheIfRequired(MappedStatement ms) { //这里获取的是二级缓存 Cache cache = ms.getCache(); if (cache != null && ms.isFlushCacheRequired()) { tcm.clear(cache); } } @Override public void setExecutorWrapper(Executor executor) { throw new UnsupportedOperationException("This method should not be called"); } } TransactionalCacheManager:缓存的事务管理器 TransactionalCacheManager被CachingExecutor持有,默认同一个SQLSession中,所有的 Cache(MapperStatement中的cache 二级缓存),都被这个缓存管理器管理public class TransactionalCacheManager { private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>(); //清除某个mapper对应的cache或注解声明的cache对应的TransactionalCache public void clear(Cache cache) { getTransactionalCache(cache).clear(); } public Object getObject(Cache cache, CacheKey key) { return getTransactionalCache(cache).getObject(key); } public void putObject(Cache cache, CacheKey key, Object value) { getTransactionalCache(cache).putObject(key, value); } //提交当前事务缓存 public void commit() { for (TransactionalCache txCache : transactionalCaches.values()) { txCache.commit(); } } //回滚所有事务缓存 public void rollback() { for (TransactionalCache txCache : transactionalCaches.values()) { txCache.rollback(); } } 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增加事务功能,简单来说即commit和rollback功能。
commit即将entriesToAddOnCommit提交到底层缓存中,
rollback即回退public class TransactionalCache implements Cache { private static final Log log = LogFactory.getLog(TransactionalCache.class); //包装类 private final Cache delegate; //是否在提交的时候清除底层缓存 private boolean clearOnCommit; //提交的时候要被添加到缓存中的对象 private final Map<Object, Object> entriesToAddOnCommit; //未命中的对象 private final Set<Object> entriesMissedInCache; public TransactionalCache(Cache delegate) { this.delegate = delegate; //默认false this.clearOnCommit = false; this.entriesToAddOnCommit = new HashMap<Object, Object>(); this.entriesMissedInCache = new HashSet<Object>(); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { return delegate.getSize(); } @Override public Object getObject(Object key) { // issue #116 //底层缓存获取为空添加到entriesMissedInCache中 Object object = delegate.getObject(key); if (object == null) { entriesMissedInCache.add(key); } // issue #146 //If a session is closed with no commit/rollback, 2nd level caches should be updated if the session just executed selects 如果clearOnCommit为true即执行过clear,clear在CachingExcutor中flushCacheIfRequired调用,在调用update方法时flushCacheIfRequired返回true if (clearOnCommit) { return null; } else { return object; } } @Override public ReadWriteLock getReadWriteLock() { return null; } //放入entriesToAddOnCommit等待提交 @Override public void putObject(Object key, Object object) { entriesToAddOnCommit.put(key, object); } @Override public Object removeObject(Object key) { return null; } //清除待提交,设置标志 @Override public void clear() { clearOnCommit = true; entriesToAddOnCommit.clear(); } //将当前队列待提交的数据提交到缓存中,并且重新设置标志,清空队列 public void commit() { if (clearOnCommit) { delegate.clear(); } flushPendingEntries(); reset(); } public void rollback() { unlockMissedEntries(); reset(); } private void reset() { clearOnCommit = false; entriesToAddOnCommit.clear(); entriesMissedInCache.clear(); } private void flushPendingEntries() { for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) { delegate.putObject(entry.getKey(), entry.getValue()); } //同时也将未命中的数据在缓存中设置为空 for (Object entry : entriesMissedInCache) { if (!entriesToAddOnCommit.containsKey(entry)) { delegate.putObject(entry, null); } } } //去掉缓存中未命中的数据 private void unlockMissedEntries() { for (Object entry : entriesMissedInCache) { try { delegate.removeObject(entry); } catch (Exception e) { log.warn("Unexpected exception while notifiying a rollback to the cache adapter." + "Consider upgrading your cache adapter to the latest version. Cause: " + e); } } } }
BaseExecutor#query@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()); //使用同一个sqlSession关闭的时候会调用Executor.close if (closed) { throw new ExecutorException("Executor was closed."); } //与之前不同的是这里清除的是一级缓存 //queryStack的作用是当多个查询使用的是同一个SqlSession即同一个Executor时, //这些查询之间是并发进行的,如果后面的某个select语句的flushCache=true会导致其他语句的缓存被清除,即一个flushCache导致其他语句缓存失效,这是应该避免的,所以有且仅仅当当前flushCache=true的语句是当前session的第一个语句或者当前语句总是在当前session最先执行,即可保证其他缓存不被清除 if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { //每执行一次++1,可判断当前是否能进行缓存清除 queryStack++; //同二级缓存只有在resultHandler为空的情况下一级缓存才保持有效 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { //如果list不为空处理callable语句 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //从数据库获取 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { //最后执行 queryStack--; } //查询完成后执行延迟加载,如果是语句中包含嵌套语句,并且嵌套语句的值已经在一级缓存中,就调用当前Executor的deferLoad生成一个,DeferredLoad包含当前返回结果的MetaObject,objectFactory,resultExtractor等。这里执行load方法就从缓存中获取嵌套的语句值设置给自己,达到加快速度的目的,queryStack=0代表着当前查询结束 if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); //如果本地缓存scope 是语句级清除当前缓存 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
SimpleExecutor#doQuery
BaseExecutor执行query真正调用的是SimpleExecutor的doQuery方法@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
Configuration#newStatementHandler 创建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //RoutingStatementHandler是几种StatementHandler的封装 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) //前面执行了Executor的拦截器,这里执行的是StatementHandler的拦截器,即此处的拦截器被当做StatementHandler interceptorChain.pluginAll(statementHandler); return statementHandler; }
SimpleExecutor#prepareStatement 创建预处理语句,设置参数private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //获取连接 Connection connection = getConnection(statementLog); //设置语句 stmt = handler.prepare(connection, transaction.getTimeout()); //设置参数 handler.parameterize(stmt); return stmt; }
BaseExecutor#getConnection
获取Connection对Connection进行动态代理,添加日志功能protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); if (statementLog.isDebugEnabled()) { return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } }
RoutingStatementHandler#prepare
调用被装饰的SatementHandler的prepare
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { return delegate.prepare(connection, transactionTimeout); }
BaseStatementHandler#preparepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } }
PreparedStatementHandler#instantiateStatement@Override protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() != null) { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.prepareStatement(sql); } }
//PreparedStatementHandler#parameterize @Override public void parameterize(Statement statement) throws SQLException { //BaseStatementHanlder构造方法 // this.parameterHandler = configuration.newParameterHandler(mappedStatement, //parameterObject, boundSql); parameterHandler.setParameters((PreparedStatement) statement); } //DefaultParameterHandler#setParameters /** 遍历parameterMappings,获取当前参数中的属性名称,如果boundsql有附加参数,第一优先级为附加参数,如果已经在Configuration中配置了自定义类型处理器,使用自定义类型处理器设置参数,否则使用MetaObject获取参数中的值,最后调用TypeHandler设置参数值,这里的BaseTypeHandler中setParameter调用子类的setNonNullParameter,至此SimpleExecutor的prepareStatement就执行完成了,SimpleExecutor的doQuery方法已经完成了StatementHandler的生成和Statement的生成,下一步就是执行带参数的Statement, statement被传入StatementHandler(RoutingStatementHandler)的query继续向下委托 **/ @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
PreparedStatementHandler#query
执行完语句,PreparedStatementHandler回调resultSetHandler的handleResultSets方法,resultSetHandler是在BaseHandler中的构造方法中进行初始化的
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
发表评论
-
MyBatis原理(2)-执行流程 4 Mapper的执行
2018-09-07 11:15 699执行方式2: DeptMapper mapper ... -
MyBatis原理(2)-执行流程 3 处理结果集
2018-09-07 10:24 814DefaultResultSetHandler#handleR ... -
MyBatis原理(2)-执行流程 1 BoundSql生成
2018-08-31 17:09 1183MyBatis执行两种方式: 1. SqlSession ... -
MyBatis原理(1)-启动流程3- mapper 加载
2018-08-25 22:27 8201.接着上一篇文章解析mapper第一步 mapperEle ... -
MyBatis原理(1)-启动流程2
2018-08-24 17:21 4941.XMLConfigBuilder.parse ... -
MyBatis原理(1)-启动流程1
2018-08-23 17:10 1070概述:本文按三个部分依次循序渐进对mybatis源码-原理进行 ...
相关推荐
- Mybatis SQL执行流程 - Executor的类型 - 什么是延迟加载? - 延迟加载原理 - ${} 和 #{}的区别 - Mybatis 模糊查询LIKE怎么写 - Mybatis是如何获取生成的主键的? - Mybatis动态SQL是什么? - Mybatis插件...
MyBatis R2DBC工作原理对数据CRUD操作SqlSession接口进行Reactive化,对于R2DBC适配来说为ReactiveSqlSessionMapper接口Reactive化,将函数的类型从对象和List调整为Mono和FluxSQL的执行器调整为R2DBC的接口,这里...
该思维导图主要是对MyBatis原理知识进行了整理,通过对底层的分析,能够实现手写一个实现核心功能的简单MyBats,内容包括MyBatis整体架构和流程的分析、SQL的解析过程、手写解析流程、手写执行流程、看源码、MyBatis...
分类专栏: java框架 文章标签: spring mvc spring mybatis 作用: SSM框架是spring MVC ,... springMVC执行流程: 1. 客户端将请求统一提交到DispatcherServlet; 2. DispatcherServlet会将请求交给HandlerMapping
自己阅读源码及查询资料所写的mybatis源码总结, 包含了mybatis一级缓存、二级缓存,及缓存中常遇到的缓存击穿、缓存雪崩、缓存穿透...挖掘mybatis从配置文件、接口到如何执行sql深层原理;常用的关联查询、嵌套查询等
提高核心竞争力,那么本套视频课程带你深入理解MyBatis、Spring及SpringMVC框架执行流程、底层原理、实现源码,让你对经典流行开源框架做到知其然亦知其所以然,让知识真正地学以致用。 其中有大量一线大厂高频面试...
│ Java面试题48.struts2的执行流程或者struts2的原理.mp4 │ Java面试题49.Struts2的拦截器是什么?你都用它干什么?.mp4 │ Java面试题50.Spring MVC的执行流程.mp4 │ Java面试题51.SpringMVC和Struts2的不同.mp4...
宏天BPM X3软件平台是一个易于部署和使用的新一代业务流程管理平台,能够以最快的速度帮助您梳理、设计、执行、监控分析和优化业务流程,从业务和IT相结合的完美视角为用户组建长期而久远的卓越BPM流程管 理架构。...
133.mybatis 分页插件的实现原理是什么? 134.mybatis 如何编写一个自定义插件? 十四、RabbitMQ 135.rabbitmq 的使用场景有哪些? 136.rabbitmq 有哪些重要的角色? 137.rabbitmq 有哪些重要的组件? 138.rabbitmq ...
hashmap的容量为什么一定要是2的幂呢 TreeMap的底层原理 HashMap,Hashtable和ConcurrentHashMap的区别 在ArrayList和LinkedList尾部添加元素,谁的效率更高 如果HashMap或者hashTable的key是一个自定义的类该怎么办 ...
其次,阐述了SpringBoot、MyBatis、EasyUI 等轻量级开源框架的工作原理,对 Maven、MySQL等管理工具的特点进行了分析。再次,分析了系统总体建设范围,通过用例图、流程图等 UML 建模方式,描述了用户可以执行的功能...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
Java-CS-Record这是一个频繁更新的项目(2020.9 ~ 2021.3)。文章说明这个仓库的文章,都是...目录基础1、Java 核心常见特性源码类型相关特性相关 反射相关问题与源码解析注解底层源码解析 Stream 执行流程源码解析 S
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
Build Execution Deployment(构建执行部署)............................................................ 48 Languages&Frameworks.............................................................................
2. 目录 .........................................................................................................................................................1 JVM ...................................