MyBatis(四) sql执行流程

时间:2022-07-25 05:10:20

1.MyBatis(三) xml文件解析流程 动态SQL解析,中介绍了MyBatis SQL的解析过程。那么MyBatis执行流程中就涉及到具体sql生成的过程了。首先看下SqlSession的获取

MyBatis(四) sql执行流程
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与SqlSessionFactorySqlSession通过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的代理类对象,具体流程如下:

MyBatis(四) sql执行流程
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的执行器。
MyBatis(四) sql执行流程

> 1.**BaseExecutor:** 作为SQL的执行器基类 2.**ReuseExecutor:**继承自**BaseExcutor**,ReuseExecutor顾名思义就是重复使用执行,其定义了一个Map

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方法, 此方法来自他的基类BaseExecutorSimpleExecutor并没有重写该方法,但是此处执行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 介绍

MyBatis(四) sql执行流程

> 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);
}