Mybatis源码解析,一步一步从浅入深(七):执行查询
一,前言
我们在文章:mybatis源码解析,一步一步从浅入深(二):按步骤解析源码的最后一步说到执行查询的关键代码:
result = sqlsession.selectone(command.getname(), param);
selelectone方法有两个参数:
第一个参数是:com.zcz.learnmybatis.dao.userdao.finduserbyid
第二个参数是:1(integer类型,就是我们传入的参数id:1,我们是期望通过这个id查询到我们想要的结果)
因为接下来的代码比较复杂,而且容易迷路。那么我们就定下来本片文章的目的:
1,sql语句是什么时候,在哪里执行的?
2,我们传入参数id是怎么参与执行的?
为了更情绪的分析这两个问题的答案,我将分析的过程分为三步:
1,在sqlsession中的执行过程
2,在excutor中的执行过程
3,在statement中的执行过程
二,在代码的执行过程中分析问题
1,代码在sqlsession中的执行过程
在文章:mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中,我们已经知道了,使用的sqlsession是defaultsqlsession,那显而易见,要首先看一下selectone的源码了:
1 defaultsqlsession implements sqlsession 2 public <t> t selectone(string statement, object parameter) { 3 // popular vote was to return null on 0 results and throw exception on too many. 4 // 执行查询 5 list<t> list = this.<t>selectlist(statement, parameter); 6 if (list.size() == 1) { 7 //如果返回的list的大小是1,则返回第一个元素 8 return list.get(0); 9 } else if (list.size() > 1) { 10 //如果大于1,则抛出异常 11 throw new toomanyresultsexception("expected one result (or null) to be returned by selectone(), but found: " + list.size()); 12 } else { 13 // 如果小于1,则返回null 14 return null; 15 } 16 }
到这里可以明白一件事情,原来selectone是调用selectlist执行查询的,只不过是取了返回值的第一个元素。
我们传入的参数id,就是第5行代码的第二个参数,继续分析第5行的源代码:
defaultsqlsession implements sqlsession public <e> list<e> selectlist(string statement, object parameter) { // 调用了另外一个三个参数的重载方法, return this.selectlist(statement, parameter, rowbounds.default); }
继续跟踪:
1 defaultsqlsession implements sqlsession 2 public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) { 3 try { 4 //从configuration中取出解析userdao-mapping.xml文件是生成的mappedstatement 5 mappedstatement ms = configuration.getmappedstatement(statement); 6 // 这里的executor是cachingexecutor 7 list<e> result = executor.query(ms, wrapcollection(parameter), rowbounds, executor.no_result_handler); 8 return result; 9 } catch (exception e) { 10 throw exceptionfactory.wrapexception("error querying database. cause: " + e, e); 11 } finally { 12 errorcontext.instance().reset(); 13 } 14 }
看源代码的第5行,还记得在文章:mybatis源码解析,一步一步从浅入深(五):mapper节点的解析中,mapper文件中的每一个select,insert,update,delete标签,都会解析成一个mappedstatement类的实例对象吗?而且这个实例对象是存放在configuration中的。然后执行第7行代码,这里的executor是cachingexecutor实例对象。到这里sqlsession中的代码流程就结束了,我们进入executor中的执行过程。
2,代码在在excutor中的执行过程
看看源代码:
1 cachingexecutor implements executor 2 public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler) throws sqlexception { 3 //取出sql语句 4 boundsql boundsql = ms.getboundsql(parameterobject); 5 cachekey key = createcachekey(ms, parameterobject, rowbounds, boundsql); 6 return query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); 7 }
同样的,查询参数id也是在这个方法的第二个参数,而且在取出sql语句的方法中,对查询参数进行了检查。继续跟踪这个方法中的第6行代码:
1 cachingexecutor implements executor 2 public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) 3 throws sqlexception { 4 //使用缓存 5 cache cache = ms.getcache(); 6 if (cache != null) { 7 flushcacheifrequired(ms); 8 if (ms.isusecache() && resulthandler == null) { 9 ensurenooutparams(ms, parameterobject, boundsql); 10 @suppresswarnings("unchecked") 11 list<e> list = (list<e>) tcm.getobject(cache, key); 12 if (list == null) { 13 list = delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); 14 tcm.putobject(cache, key, list); // issue #578. query must be not synchronized to prevent deadlocks 15 } 16 return list; 17 } 18 } 19 20 return delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); 21 }
继续跟踪第20行代码:
1 baseexecutor implements executor 2 @suppresswarnings("unchecked") 3 public <e> list<e> query(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) throws sqlexception { 4 errorcontext.instance().resource(ms.getresource()).activity("executing a query").object(ms.getid()); 5 if (closed) throw new executorexception("executor was closed."); 6 if (querystack == 0 && ms.isflushcacherequired()) { 7 clearlocalcache(); 8 } 9 list<e> list; 10 try { 11 querystack++; 12 list = resulthandler == null ? (list<e>) localcache.getobject(key) : null; 13 if (list != null) { 14 handlelocallycachedoutputparameters(ms, key, parameter, boundsql); 15 } else { 16 list = queryfromdatabase(ms, parameter, rowbounds, resulthandler, key, boundsql); 17 } 18 } finally { 19 querystack--; 20 } 21 if (querystack == 0) { 22 for (deferredload deferredload : deferredloads) { 23 deferredload.load(); 24 } 25 deferredloads.clear(); // issue #601 26 if (configuration.getlocalcachescope() == localcachescope.statement) { 27 clearlocalcache(); // issue #482 28 } 29 } 30 return list; 31 }
继续跟踪第16行代码:
1 baseexecutor implements executor 2 private <e> list<e> queryfromdatabase(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) throws sqlexception { 3 list<e> list; 4 localcache.putobject(key, execution_placeholder); 5 try { 6 list = doquery(ms, parameter, rowbounds, resulthandler, boundsql); 7 } finally { 8 localcache.removeobject(key); 9 } 10 localcache.putobject(key, list); 11 if (ms.getstatementtype() == statementtype.callable) { 12 localoutputparametercache.putobject(key, parameter); 13 } 14 return list; 15 }
继续跟踪第6行代码:
1 simpleexecutor extends baseexecutor 2 public <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception { 3 statement stmt = null; 4 try { 5 configuration configuration = ms.getconfiguration(); 6 statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql); 7 //准备预处理语句 8 stmt = preparestatement(handler, ms.getstatementlog()); 9 return handler.<e>query(stmt, resulthandler); 10 } finally { 11 closestatement(stmt); 12 } 13 }
看到第3行了吗?这里声明了一个statement,是不是很熟悉?是的,这就是我们在使用原始的jdbc进行查询的时候,用到的,那么我们的问题是不是在这里就有了答案了呢?这里先留一个标记。
代码执行到这里之后呢,executor部分的流程就结束了,接下来是在statement中的执行过程。
3,代码在statement中的执行过程
继续看源码:
1 //第一段源码 2 routingstatementhandler implements statementhandler 3 public <e> list<e> query(statement statement, resulthandler resulthandler) throws sqlexception { 4 return delegate.<e>query(statement, resulthandler); 5 } 6 //第二段源码 7 preparedstatementhandler extends basestatementhandler 8 public <e> list<e> query(statement statement, resulthandler resulthandler) throws sqlexception { 9 preparedstatement ps = (preparedstatement) statement; 10 ps.execute(); 11 return resultsethandler.<e> handleresultsets(ps); 12 }
在源代码的第10行,是不是也很熟悉?同样也是使用了jdbc进行的查询。似乎这一段的源代码很简单,但其实不是的resultsethandler中也是做了一部分工作的,这里就不详细描述了。代码到这里就结束了,似乎我们没有看到文章开头的两个问题的答案。看来应该就是在上面标记的地方了。
4,问题的答案
关键代码:stmt = preparestatement(handler, ms.getstatementlog());
preparestatement源码:
1 private statement preparestatement(statementhandler handler, log statementlog) throws sqlexception { 2 statement stmt; 3 //从事务中获取数据库连接 4 connection connection = getconnection(statementlog); 5 // 获取statement 6 stmt = handler.prepare(connection); 7 // 为statement设置查询参数 8 handler.parameterize(stmt); 9 return stmt; 10 }
1 public statement prepare(connection connection) throws sqlexception { 2 errorcontext.instance().sql(boundsql.getsql()); 3 statement statement = null; 4 try { 5 //初始化statement 6 statement = instantiatestatement(connection); 7 //设置查询超时时间 8 setstatementtimeout(statement); 9 setfetchsize(statement); 10 return statement; 11 } catch (sqlexception e) { 12 closestatement(statement); 13 throw e; 14 } catch (exception e) { 15 closestatement(statement); 16 throw new executorexception("error preparing statement. cause: " + e, e); 17 } 18 }
1 protected statement instantiatestatement(connection connection) throws sqlexception { 2 string sql = boundsql.getsql(); 3 if (mappedstatement.getkeygenerator() instanceof jdbc3keygenerator) { 4 string[] keycolumnnames = mappedstatement.getkeycolumns(); 5 if (keycolumnnames == null) { 6 return connection.preparestatement(sql, preparedstatement.return_generated_keys); 7 } else { 8 return connection.preparestatement(sql, keycolumnnames); 9 } 10 } else if (mappedstatement.getresultsettype() != null) { 11 return connection.preparestatement(sql, mappedstatement.getresultsettype().getvalue(), resultset.concur_read_only); 12 } else { 13 //从数据库连接中获取statement 14 return connection.preparestatement(sql); 15 } 16 }
看到第14行,哈哈这里就是我们要的答案了。这样一来我们的两个问题,就都有答案了。
因为执行查询的这个过程比较复杂,如果真的要详细的全部解释清楚的话,估计还得10几篇文章要写。鉴于作者能力和时间,就大概的展示一下这个过程吧。
远程不易,转载请声明出处:https://www.cnblogs.com/zhangchengzi/p/9712446.html