[置顶] MyBatis(四) sql执行流程
2017-05-26 15:58
435 查看
1.MyBatis(三) xml文件解析流程 动态SQL解析,中介绍了MyBatis SQL的解析过程。那么MyBatis执行流程中就涉及到具体sql生成的过程了。首先看下SqlSession的获取
1.1 首先调用SqlSessionFactoryBuilder的build方法来初始化加载配置文件这里前面已经分析过了,此处给返回SqlSessionFactory对象
1.2 通过DefaultSqlSessionFactory的openSession方法,实际调用的是它的openSessionFromDataSource方法,这里返回SqlSession ,具体来说是返回的是SqlSession 的实现类DefaultSqlSession
2.SqlSession与SqlSessionFactory:SqlSession通过SqlSessionFactory的opensession方法返回的
当我们得到SqlSession通过getMapper方法来获取dao最后通过dao来执行相应的数据库代码,其实这里获取是Mapper也就是dao的代理类对象,具体流程如下:
2.1 首先通过SqlSession这个具体实现类是DefaultSqlSession的getMapper来获取代理对象,
2.2 然而DefaultSqlSession什么都不做,将mapper的获取交给了Configuration了。Configuration的getMapper方法
2.3 接着又被转交给MapperRegistry对象的getMapper方法
2.4 MapperProxyFactory的newInstance方法,我们可以看出这里使用了动态代理机制,而代理类就是MapperProxy
3 MapperProxy
3.1 sql真正调用开始的地方,MapperProxy的invoke方法最终会被代理对象调用
3.2 MapperMethod 处理mapper接口的方法,调用 mapperMethod.execute(sqlSession, args);此处根据不同的sql语句类型来处理调用sqlSession对应的方法,而这个SqlCommandType从哪里来,答案就在前面的xml解析中的MappedStatement对象,而MappedStatement又保存在Configuration中,前面说过Configuration是MyBatis这个调度配置中心,详情可以MapperMethod构造方法中查看SqlCommand对象。这里就不展开
4.回到SqlSession的实现类DefaultSqlSession,只分析一个方法其他的都类似,MyBatis(三) xml文件解析流程 动态SQL解析文中保存了各个SqlNode都在等待着MixedSqlNode的apply方法。
4.1 Executor: SQL的执行器。
1.BaseExecutor: 作为SQL的执行器基类
2.ReuseExecutor:继承自BaseExcutor,ReuseExecutor顾 名思义就是重复使用执行,其定义了一个Map,将执行的sql作为key,将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了。
3. BatchExecutor:批量处理器
4. SimpleExecutor:简单处理器,也是默认的sql执行器
5. CachingExecutor: 这个执行器跟二级缓存有关,当开启二级缓存是就会使用这个SQL执行器。
4.2 SimpleExecutor默认sql执行器的创建在Configuration中的newExecutor方法中
4.3 SimpleExecutor默认sql执行器的query方法, 此方法来自他的基类BaseExecutor ,SimpleExecutor并没有重写该方法,但是此处执行BaseExecutor此方法的意义就是sql的生成。其中BoundSql 类中包含了sql
这里稍微提下Mybatis一级缓存,上面方法中用到了一级缓存,后面会说到。
4.3.1 BoundSql
4.3.2 BoundSql 通过SqlSource(实现类DynamicSqlSource)的getBoundSql方法获取,取得了BoundSql也就取得了动态生成后的sql语句就可以交给Executor执行了。
4.3.3
在XMLScriptBuilder中的此方法中就已经保存到了 SqlSource MixedSqlNode 会根据 SqlNode的不同执行不同类别的sqlNode的apply方法正真根据参数生成可执行sql语句
4.4 BaseExecutor的 queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
4.5 doquery方法,BaseExecutor的doquery为抽象方法,所以具体实现在它的非抽象子类SimpleExecutor默认sql执行器中
5 . StatementHandler 介绍
> 1.**SimpleStatementHandler:** 其实就是对jdbc **Statement**的封装
2.**PreparedStatementHandler:** 其实就是对jdbc **PreparedStatement**的封装,对sql进行预编译,防止sql注入
3.**CallableStatementHandler:** 其实就是对jdbc 的**CallableStatement**的封装,对调用存储过程时使用
4 . **RoutingStatementHandler:**作为其他Statement的调度中心
5.1**MappedStatement**中默认设置StatementType为PREPARED
5.2 当追踪查询语句执行时返现MappedStatement的创建在 Configuration中的newStatementHandler方法中调用 RoutingStatementHandler的构造方法中创建的
5.3 sql执行StatementHandler的query方法,以 PreparedStatementHandler为例,这里是不是很熟悉,这不就是jdbc的操作嘛
1.1 首先调用SqlSessionFactoryBuilder的build方法来初始化加载配置文件这里前面已经分析过了,此处给返回SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
1.2 通过DefaultSqlSessionFactory的openSession方法,实际调用的是它的openSessionFromDataSource方法,这里返回SqlSession ,具体来说是返回的是SqlSession 的实现类DefaultSqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType, autoCommit); return new DefaultSqlSession(configuration, executor); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
2.SqlSession与SqlSessionFactory:SqlSession通过SqlSessionFactory的opensession方法返回的
private final static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatisConfig.xml";
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
System.out.println(e.getMessage());
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);}
SqlSession sqlSession = sqlSessionFactory.openSession(); VmanBomDao vmanBomDao = sqlSession.getMapper(VmanBomDao.class); int i = vmanBomDao.insertVmanBomBean(vmanBomBean);
当我们得到SqlSession通过getMapper方法来获取dao最后通过dao来执行相应的数据库代码,其实这里获取是Mapper也就是dao的代理类对象,具体流程如下:
2.1 首先通过SqlSession这个具体实现类是DefaultSqlSession的getMapper来获取代理对象,
public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
2.2 然而DefaultSqlSession什么都不做,将mapper的获取交给了Configuration了。Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
2.3 接着又被转交给MapperRegistry对象的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
2.4 MapperProxyFactory的newInstance方法,我们可以看出这里使用了动态代理机制,而代理类就是MapperProxy
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
3 MapperProxy
3.1 sql真正调用开始的地方,MapperProxy的invoke方法最终会被代理对象调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //因为所有的类都继承自Object对象,如果没有此处的代码则tostring等方法调用都只会返回null if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } //处理Mapper方法 final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
3.2 MapperMethod 处理mapper接口的方法,调用 mapperMethod.execute(sqlSession, args);此处根据不同的sql语句类型来处理调用sqlSession对应的方法,而这个SqlCommandType从哪里来,答案就在前面的xml解析中的MappedStatement对象,而MappedStatement又保存在Configuration中,前面说过Configuration是MyBatis这个调度配置中心,详情可以MapperMethod构造方法中查看SqlCommand对象。这里就不展开
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
4.回到SqlSession的实现类DefaultSqlSession,只分析一个方法其他的都类似,MyBatis(三) xml文件解析流程 动态SQL解析文中保存了各个SqlNode都在等待着MixedSqlNode的apply方法。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
4.1 Executor: SQL的执行器。
1.BaseExecutor: 作为SQL的执行器基类
2.ReuseExecutor:继承自BaseExcutor,ReuseExecutor顾 名思义就是重复使用执行,其定义了一个Map,将执行的sql作为key,将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了。
3. BatchExecutor:批量处理器
4. SimpleExecutor:简单处理器,也是默认的sql执行器
5. CachingExecutor: 这个执行器跟二级缓存有关,当开启二级缓存是就会使用这个SQL执行器。
4.2 SimpleExecutor默认sql执行器的创建在Configuration中的newExecutor方法中
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
4.3 SimpleExecutor默认sql执行器的query方法, 此方法来自他的基类BaseExecutor ,SimpleExecutor并没有重写该方法,但是此处执行BaseExecutor此方法的意义就是sql的生成。其中BoundSql 类中包含了sql
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } 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一级缓存中获取 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //如果一级缓存中为null则执行查询数据库 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); // issue #601 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); // issue #482 } } return list; }
这里稍微提下Mybatis一级缓存,上面方法中用到了一级缓存,后面会说到。
4.3.1 BoundSql
public class BoundSql { private String sql; private List<ParameterMapping> parameterMappings; private Object parameterObject; private Map<String, Object> additionalParameters; private MetaObject metaParameters; }
4.3.2 BoundSql 通过SqlSource(实现类DynamicSqlSource)的getBoundSql方法获取,取得了BoundSql也就取得了动态生成后的sql语句就可以交给Executor执行了。
public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); //这里的rootSqlNode就是前文中的MixedSqlNode rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql; }
4.3.3
在XMLScriptBuilder中的此方法中就已经保存到了 SqlSource MixedSqlNode 会根据 SqlNode的不同执行不同类别的sqlNode的apply方法正真根据参数生成可执行sql语句
public SqlSource parseScriptNode() { List<SqlNode> contents = parseDynamicTags(context); MixedSqlNode rootSqlNode = new MixedSqlNode(contents); SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode); return sqlSource; }
4.4 BaseExecutor的 queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
4.5 doquery方法,BaseExecutor的doquery为抽象方法,所以具体实现在它的非抽象子类SimpleExecutor默认sql执行器中
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { //MappedStatement 来自Configuration,同时MappedStatement 中也包含Configuration,通过MappedStatement 来获取Configuration对象 Configuration configuration = ms.getConfiguration(); //StatementHandler 获取 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
5 . StatementHandler 介绍
> 1.**SimpleStatementHandler:** 其实就是对jdbc **Statement**的封装
2.**PreparedStatementHandler:** 其实就是对jdbc **PreparedStatement**的封装,对sql进行预编译,防止sql注入
3.**CallableStatementHandler:** 其实就是对jdbc 的**CallableStatement**的封装,对调用存储过程时使用
4 . **RoutingStatementHandler:**作为其他Statement的调度中心
5.1**MappedStatement**中默认设置StatementType为PREPARED
mappedStatement.statementType = StatementType.PREPARED;
5.2 当追踪查询语句执行时返现MappedStatement的创建在 Configuration中的newStatementHandler方法中调用 RoutingStatementHandler的构造方法中创建的
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
5.3 sql执行StatementHandler的query方法,以 PreparedStatementHandler为例,这里是不是很熟悉,这不就是jdbc的操作嘛
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
相关文章推荐
- [置顶] MyBatis(四) sql执行流程
- MyBatis-Spring 执行SQL语句的流程
- MyBatis主流程分析之(三)-准备SQL语句和参数替换、执行
- [置顶] 【p6spy】程序员开发利器P6spy——打印执行sql语句,mybatis、ibatis、Hibernate均可使用
- Mybatis源码(三)之SqlSession执行流程全貌
- Mybatis 源码 sql执行流程分析
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- MyBatis源码学习笔记(十)SQL执行流程分析
- Mybatis-SQL执行流程分析
- [置顶] 【MySql】Sql优化(一)——Sql执行流程
- Mybatis3.3.x技术内幕(十一):执行一个Sql命令的完整流程
- MyBatis源码分析-SQL语句执行的完整流程
- MyBatis-Spring 执行SQL语句的流程
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- mybatis源码分析,sql语句执行的完整流程
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)