Mybatis源码解析(六)

开课吧开课吧锤锤2021-03-03 11:07

    Java是一门全球范围内使用最广泛的,面向对象的编程语言。Java语言具有功能强大和简单易用两个特征,它作为面向对象编程语言系列的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。

Java

    五、执行查询语句

    我们知道,我们获取到的UserMapper实际上是代理对象MapperProxy,所以我们执行查询语句的时候实际上执行的是MapperProxy的invoke方法:

    publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

    /**如果调用的是Object原生的方法,则直接放行*/

    if(Object.class.equals(method.getDeclaringClass())){

    try{

    returnmethod.invoke(this,args);

    }catch(Throwablet){

    throwExceptionUtil.unwrapThrowable(t);

    }

    }

    finalMapperMethodmapperMethod=cachedMapperMethod(method);

    returnmapperMethod.execute(sqlSession,args);

    }

    我们再来看看cachedMapperMethod方法:

    privateMapperMethodcachedMapperMethod(Methodmethod){

    MapperMethodmapperMethod=methodCache.get(method);

    if(mapperMethod==null){

    mapperMethod=newMapperMethod(mapperInterface,method,sqlSession.getConfiguration());

    methodCache.put(method,mapperMethod);

    }

    returnmapperMethod;

    }

    可以看到,先根据方法签名,从方法缓存中获取方法,如果为空,则生成一个MapperMethod放入缓存并返回。

    所以最终执行查询的是MapperMethod的execute方法:

    publicObjectexecute(SqlSessionsqlSession,Object[]args){

    Objectresult;

    if(SqlCommandType.INSERT==command.getType()){

    Objectparam=method.convertArgsToSqlCommandParam(args);

    result=rowCountResult(sqlSession.insert(command.getName(),param));

    }elseif(SqlCommandType.UPDATE==command.getType()){

    Objectparam=method.convertArgsToSqlCommandParam(args);

    result=rowCountResult(sqlSession.update(command.getName(),param));

    }elseif(SqlCommandType.DELETE==command.getType()){

    Objectparam=method.convertArgsToSqlCommandParam(args);

    result=rowCountResult(sqlSession.delete(command.getName(),param));

    /**select查询语句*/

    }elseif(SqlCommandType.SELECT==command.getType()){

    /**档返回类型为空*/

    if(method.returnsVoid()&&method.hasResultHandler()){

    executeWithResultHandler(sqlSession,args);

    result=null;

    /**当返回many的时候*/

    }elseif(method.returnsMany()){

    result=executeForMany(sqlSession,args);

    /**当返回值类型为Map时*/

    }elseif(method.returnsMap()){

    result=executeForMap(sqlSession,args);

    }else{

    /**除去以上情况,执行这里的步骤*/

    Objectparam=method.convertArgsToSqlCommandParam(args);

    result=sqlSession.selectOne(command.getName(),param);

    }

    }else{

    thrownewBindingException("Unknownexecutionmethodfor:"+command.getName());

    }

    if(result==null&&method.getReturnType().isPrimitive()&&!method.returnsVoid()){

    thrownewBindingException("Mappermethod'"+command.getName()

    +"attemptedtoreturnnullfromamethodwithaprimitivereturntype("+method.getReturnType()+").");

    }

    returnresult;

    }

    我们示例中执行的分支语句是:

    Objectparam=method.convertArgsToSqlCommandParam(args);

    result=sqlSession.selectOne(command.getName(),param);

    这里有一个查询参数的解析过程:

    publicObjectconvertArgsToSqlCommandParam(Object[]args){

    finalintparamCount=params.size();

    if(args==null||paramCount==0){

    returnnull;

    /**参数没有标注@Param注解,并且参数个数为一个*/

    }elseif(!hasNamedParameters&&paramCount==1){

    returnargs[params.keySet().iterator().next()];

    /**否则执行这个分支*/

    }else{

    finalMap<String,Object>param=newParamMap<Object>();

    inti=0;

    for(Map.Entry<Integer,String>entry:params.entrySet()){

    param.put(entry.getValue(),args[entry.getKey()]);

    //issue#71,addparamnamesasparam1,param2...butensurebackwardcompatibility

    finalStringgenericParamName="param"+String.valueOf(i+1);

    if(!param.containsKey(genericParamName)){

    param.put(genericParamName,args[entry.getKey()]);

    }

    i++;

    }

    returnparam;

    }

    }

    这里的代码看起来可能逻辑不太好懂,我这里解释一下结论就好,这里参数解析如果判断参数一个只有一个(一个单一参数或者是一个集合参数),并且没有标注@Param注解,那么直接返回这个参数的值,否则会被封装为一个Map,然后再返回。

    封装的样式如下用几个示例解释:

    例1:

    /**接口为*/

    UserselectByNameSex(Stringname,intsex);

    /**我们用如下格式调用*/

    userMapper.selectByNameSex("张三",0);

    /**参数会被封装为如下格式:*/

    0--->张三

    1--->0

    param1--->张三

    param2--->0

    如下图所示:

Java

    例2:

    /**接口为*/

    UserselectByNameSex(@Param("name")Stringname,intsex);

    /**我们用如下格式调用*/

    userMapper.selectByNameSex("张三",0);

    /**参数会被封装为如下格式:*/

    name--->张三

    1--->0

    param1--->张三

    param2--->0

    如下图所示:

Java

    参数处理完接下来就是调用执行过程,最终调用执行的是DefaultSqlSession中的selectList方法:

    public<E>List<E>selectList(Stringstatement,Objectparameter,RowBoundsrowBounds){

    try{

    MappedStatementms=configuration.getMappedStatement(statement);

    List<E>result=executor.query(ms,wrapCollection(parameter),rowBounds,Executor.NO_RESULT_HANDLER);

    returnresult;

    }catch(Exceptione){

    throwExceptionFactory.wrapException("Errorqueryingdatabase.Cause:"+e,e);

    }finally{

    ErrorContext.instance().reset();

    }

    }

    这里调用SimpleExecutor的query方法执行查询操作,接着调用doQuery方法:

    public<E>List<E>doQuery(MappedStatementms,Objectparameter,

RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql)throwsSQLException{

    Statementstmt=null;

    try{

    Configurationconfiguration=ms.getConfiguration();

    /**这里出现了Mybatis四大对象中的StatementHandler*/

    StatementHandlerhandler=configuration.newStatement Handler(wrapper,ms,parameter,rowBounds,resultHandler,boundSql);

    stmt=prepareStatement(handler,ms.getStatementLog());

    returnhandler.<E>query(stmt,resultHandler);

    }finally{

    closeStatement(stmt);

    }

    }

    publicStatementHandlernewStatementHandler(Executorexecutor, MappedStatementmappedStatement,ObjectparameterObject,RowBoundsrowBounds, ResultHandlerresultHandler,BoundSqlboundSql){

    StatementHandlerstatementHandler=newRoutingStatementHandler (executor,mappedStatement,parameterObject,rowBounds,resultHandler,boundSql);

    statementHandler=(StatementHandler)

    /**创建StatementHandler并应用到插件支持*/

    interceptorChain.pluginAll(statementHandler);

    returnstatementHandler;

    }

    在创建StatementHandler的同时,应用插件功能,同时创建了Mybatis四大对象中的另外两个对象:

    protectedBaseStatementHandler(Executorexecutor,MappedStatementmappedStatement, ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql){

    ……

    ……

    ……

    /**Mybatis四大对象中的ParameterHandler*/

    this.parameterHandler=configuration.newParameterHandler

(mappedStatement,parameterObject,boundSql);

    /**Mybatis四大对象中的ResultSetHandler*/

    this.resultSetHandler=configuration.newResultSetHandler(executor,mappedStatement,rowBounds, parameterHandler,resultHandler,boundSql);

    }

    同时在创建的时候,也运用到了插件增强功能:

    publicParameterHandlernewParameterHandler(MappedStatementmappedStatement, ObjectparameterObject,BoundSqlboundSql){

    ……

    ……

    ……

    interceptorChain.pluginAll(parameterHandler);

    returnparameterHandler;

    }

    publicResultSetHandlernewResultSetHandler (Executorexecutor,MappedStatementmappedStatement,RowBoundsrowBounds,

ParameterHandlerparameterHandler,

    ResultHandlerresultHandler,BoundSqlboundSql){

    ……

    ……

    ……

    interceptorChain.pluginAll(resultSetHandler);

    returnresultSetHandler;

    }

    接下来就是执行正常的JDBC查询功能了,参数设置由ParameterHandler操作,结果集处理由ResultSetHandler处理。至此,整个查询流程结束。

    整个查询阶段的时序图可以用如下图表示:

Java

    整个查询流程,可以总结如下图:

Java

    以上内容由开课吧老师左撇子小哥哥提供,更多Java教程尽在开课吧广场Java教程频道。

有用
分享