在上一篇中分析了XXXMapper接口的获取,其实质上拿到的是一个MapperProxy。这一篇主要分析sql的执行。
MapperProxy.java
private final SqlSession sqlSession;
//接口名字记录在这里
private final Class mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method); // 这里获取到了我们在Mapper接口中定义的方法;cachedMapperMethod()重点在分析,见分隔符
//args 即传入的参数
return mapperMethod.execute(this.sqlSession, args);
}
}
可以看到MapperProxy实现了InvocationHandler接口。
当我们调用List
时,就触发了代理的执行。//更正,为了看到参数的解析,后面使用User user = userMapper.findById(1)
.。
在cachedMapperMethod(Method method)
方法中可以看到,首先回去缓存中拿这个方法,没有则新建MapperMethod并放入缓存。
MapperMethod中保存着sqlCommand的类型[SELECT在后面执行sql时会用到]与调用方法名findAll。
到这里为止,我们拿到了MapperPrxoy代理和MapperMethod,就知道需要执行的对应mapper.xml文件中的对应的sql语句了。
=================== 欢乐的分隔符 ===========================
重点来看一下cachedMapperMethod(Method method)方法。
MapperProxy.java
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//重点在这里
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
来看MapperMethod的构造方法
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
如上图所示,在MapperMethod有3个静态类ParamMap、SqlCommand和MethodSignature。
(1)SqlCommand中String name
保存的是我们调用的方法,SqlCommandType保存sql语句的类型。如图:
(2)MethodSignature中保存着方法调用的返回值等信息。
(3)ParamMap保存着参数信息
=============== 分隔符结束啦 ====================
接下来到mapperMethod.execute(this.sqlSession, args)
的执行。
MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(MapperMethod.SyntheticClass_1.$SwitchMap$org$apache$ibatis$mapping$SqlCommandType[this.command.getType().ordinal()]) {
case 1:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case 2:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case 3:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case 4:
if(this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if(this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if(this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if(this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case 5:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method \'" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
在这里,根据判断选择执行的类型,本例中调用了
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
,其实最终都调用了DefaultSqlSession接口中的对应的方法。
DefaultSqlSession.java
public T selectOne(String statement, Object parameter) {
//实际上调用了selectList
List list = this.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;
}
}
public List selectList(String statement, Object parameter) {
//使用默认的分页参数
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//RowBounds主要用来分页。
public RowBounds() {
this.offset = 0;
this.limit = 2147483647;
}
//最终调用
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//重点:将Mapper接口和Mapper.xml文件映射关系对应起来
MappedStatement e = this.configuration.getMappedStatement(statement);
//调用Executor的query去执行sql,下一篇分析
var5 = this.executor.query(e, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
首先是获取了Configuration中的 Map
,然后通过statement作为key获取MappedStatement。
获取到的MappedStatement如下图:
============= sql 的执行 =================
下面分析这句代码的执行:
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
首先来看Executor接口,
上一篇我们断电跟踪到了this.executor.query(e, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)
。调用了CachingExecutor中的public
ResultHandler resultHandler)
CachingExecutor.java
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
ResultHandler resultHandler) throws SQLException {
//拼接Sql,在这里将我们xml中定义的sql语句与传入的参数进行拼接
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
CachingExecutor.java
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) {
this.flushCacheIfRequired(ms);
if(ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, parameterObject, boundSql);
List list = (List)this.tcm.getObject(cache, key);
if(list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
//缓存中没有数据时,直接委托给Executor去执行sql的查询操作
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor.java
public List 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(this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if(this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null?(List)this.localCache.getObject(key):null;
if(list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//调用queryFromDatabase
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if(this.queryStack == 0) {
Iterator i$ = this.deferredLoads.iterator();
while(i$.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if(this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
BaseExecutor.java
private List queryFromDatabase(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
//调用SimpleExecutor中的doQuery
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if(ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
SimpleExecutor.java
public List doQuery(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
再来看stmt = this.prepareStatement(handler, ms.getStatementLog());
SimpleExecutor.java
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//从dataSource中获取connection
Connection connection = getConnection(statementLog);
//准备Statement
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
RoutingStatementHandler.java
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
BaseStatementHandler.java
//这里同样用到了模板方法模式
@Override
public 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.java
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
//获取预编译的sql语句
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);
}
}
至此stmt = handler.prepare(connection, transaction.getTimeout());
的调用结束,返回含有预编译sql语句的Statement。
那么接下来就是将预编译的sql编译成完整的sql语句了。
handler.parameterize(stmt);
方法正是为了设置参数。
DefaultParameterHandler.java
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List 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);
}
}
}
}
}
在DefaultParameterHandler中对PrepareStatemet中的?做了替换,使得stat中的sql语句变得完整。
Now,我们有了包含完整sql语句的Statement了。使用过JDBC的话,我们都知道现在可以调用stat.execute()。在这里Mybatis使用MappedStatement#query(Statement statement, ResultHandler resultHandler)
去执行sql。
PreparedStatementHandler.java
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
实际上使用了JDBC的PreparedStatement 执行了Sql语句。并将结果返回。
这一篇中核心的StateMentHandler、MapperProxy等相关类,后面专门写一篇分析。
接下来就是返回结果的处理了,具体代码在下一篇中分析。