在上一篇中我们已经分析到了我们返回的Mapper其实是个代理对象,
StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);
List<Student> students=studentMapper.findAllStudents();
studentMapper.findAllStudents();这里就会触发代理类中Invoke方法,在Invoke方法最后是mapperMethod.execute(this.sqlSession, args); 让我们以这个为入口详细分析
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if(SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if(SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
} else if(SqlCommandType.DELETE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
} else if(SqlCommandType.SELECT == this.command.getType()) {
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 {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
} else {
if(SqlCommandType.FLUSH != this.command.getType()) {
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
result = sqlSession.flushStatements();
}
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;
}
}
上面代码流程首先判断该Sql类型是什么类型,是增删改查哪一个,我们这里以insert举例子
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
首先第一步参数转换,转换成我们需要的SqlCommand,具体进入代码分析,下面的代码是我们MethodSignature中的,上一篇我们也讲到用来保存结果集和参数集:
public Object convertArgsToSqlCommandParam(Object[] args) {
int paramCount = this.params.size();
if(args != null && paramCount != 0) {
if(!this.hasNamedParameters && paramCount == 1) {
return args[((Integer)this.params.keySet().iterator().next()).intValue()];
} else {
MapperMethod.ParamMap param = new MapperMethod.ParamMap();
int i = 0;
for(Iterator i$ = this.params.entrySet().iterator(); i$.hasNext(); ++i) {
Entry entry = (Entry)i$.next();
param.put(entry.getValue(), args[((Integer)entry.getKey()).intValue()]);
String genericParamName = "param" + String.valueOf(i + 1);
if(!param.containsKey(genericParamName)) {
param.put(genericParamName, args[((Integer)entry.getKey()).intValue()]);
}
}
return param;
}
} else {
return null;
}
}
上面的过程简化为:
1.得到该方法参数的个数。
2.如果为0就返回Null。
3.如果不为0就判断是否为1,证明只有参数,所以不需要map直接返回。
4.大于1,这时候参数就不止一个,需要参数解析寻找,创建一个HashMap来装载参数,然后遍历我们之前的那个参数Map,之前的参数Map是一个TreeMap,为什么要用TreeMap我们可以把每个参数的顺序都编个号然后顺序存储在TreeMap之中。
5.在每一次遍历中我们会添加两次参数,一次是以参数名为key,参数值为Value,还有一次是以”param” + String.valueOf(i + 1)为key,,参数值为Value。
6.返回param Map。
参数转换完之后,接下来就会执行:
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
结果返回多少条。核心的是sqlSession.insert(this.command.getName(),param);
进入我们之前的DefaultSession中
public int insert(String statement, Object parameter) {
return this.update(statement, parameter);
}
对于我们的增删改都归纳为更新,进入我们的update
public int update(String statement, Object parameter) {
int var4;
try {
this.dirty = true;
MappedStatement e = this.configuration.getMappedStatement(statement);
var4 = this.executor.update(e, this.wrapCollection(parameter));
} catch (Exception var8) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);
} finally {
ErrorContext.instance().reset();
}
return var4;
}
1.首先设置dirty为true,用来判断当前能否被提交的一个标志位。
2.从Configuration中获取MappedStatement(包含当前sql语句的所有配置信息)
3.利用executor执行,这也是我们核心的执行地方。
进入其中到我们的BaseExecutor
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if(this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
this.clearLocalCache();
return this.doUpdate(ms, parameter);
}
}
this.doUpdate(ms, parameter)核心代码,是一个模版方法在Mybatis中有3个实现,Simple,Reuse,Batch 我们进入默认的Simple中:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
int var6;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var6 = handler.update(stmt);
} finally {
this.closeStatement(stmt);
}
return var6;
}
对于executor其实也是交给了我们handler去完成,下面进入handler生成方法中:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
RoutingStatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler1 = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler1;
}
对于这里我们会生成statementHandler,然后加入拦截器链,我们可以在执行之前对我们的sql做一些拦截处理改写等。在InterceptorChain中有:
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList();
public InterceptorChain() {
}
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)i$.next();
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
this.interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(this.interceptors);
}
}
让每个拦截器都对目标进行一次代理这里是利用了包装模式,以此来形成代理链,这里我们利用拦截器可以实现我们的分页插件。这里我们抛开拦截器不谈,直接进入真正逻辑的地方:
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();//得到我们的sql
Object parameterObject = boundSql.getParameterObject();//得到Sql中需要的参数
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();//主键生成策略
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);//执行
rows = statement.getUpdateCount();//得到我们更新的行数
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
首先这里的boundSql得到的sql已经是预处理了:
boundSql = mappedStatement.getBoundSql(parameterObject);在我们这句话中,它触发了sql 的解析,在解析sql的过程中,TypeHandler也被决断出来了,决断的原则就是根据参数的类型和参数对应的JDBC类型决定使用哪个TypeHandler。比如:参数类型是String的话就用StringTypeHandler,参数类型是整数的话就用IntegerTypeHandler等。解析完Sql之后我们也会填充参数。
接下来执行完后会进入我们的结果转换这里我们是插入,返回的时影响的行数,所以不需要转换。