Mybatis源码分析之执行完整分析

时间:2022-05-23 16:55:30

在上一篇中我们已经分析到了我们返回的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之后我们也会填充参数。

接下来执行完后会进入我们的结果转换这里我们是插入,返回的时影响的行数,所以不需要转换。