开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第39天,点击查看活动详情
广州爆炸了,希望疫情赶紧过去
MyBatis源码初步
前面看到了MyBatis是怎么将SQL和mapper中的方法对应起来的,接下来接着往下看。
SQL语句发送
MyBatis获取到具体的SQL之后,调用方法就可以获取xml文件中的SQL操作数据库了,但是SQL是怎么发送的呢?我们调用mapper的方法之后会通过MapperMethod的execute方法,我们继续往下走,看看SQL语句。
通过查询往下走发现,最终由一个executor发送了语句。该方法由两个实现,这里我们看下缓存的实现:
BoundSql是经过层层转化后去除掉 if、where等标签的 SQL语句,而 CacheKey是为该次查询操作计算出来的缓存键。
MyBatis查看当前的查询操作是否命中缓存。如果是,则从缓存中获取数据结果;否则,便通过 delegate调用 query方法。
通过追踪代码,断点会进入queryFromDatabase方法,其中MyBatis先在缓存中放置一个占位符,然后调用 doQuery方法实际执行查询操作。最后,又把缓存中的占位符替换成真正的查询结果。
最后生成了Statement类,Statement类并不是MyBatis中的类,而是java.sql包中的类。Statement类能够执行静态 SQL语句并返回结果。
程序还通过 Configuration的 newStatementHandler方法获得了一个StatementHandler对象 handler,然后将查询操作交给 StatementHandler对象进行。StatementHandler是一个语句处理器类,其中封装了很多语句操作方法,这里先不细究。继续往下看。经过多次跳转后,程序执行到了PreparedStatementHandler类中的代码3-32所示的方法中。
这里 ps.execute()真正执行了 SQL 语句,然后把执行结果交给ResultHandler 对象处理。而PreparedStatement类并不是MyBatis中的类,因而ps.execute()的执行不再由MyBatis负责,而是由 com.mysql.cj.jdbc包中的类负责。
查询完成之后的结果放在 PreparedStatement对象中,通过调试工具可以看到其中包含了这次查询得到的数据库字段信息、数据记录信息等。
这一步数据库查询操作涉及的方法较多。整个流程的关键步骤如下。
- 在进行数据库查询前,先查询缓存;如果确实需要查询数据库,则数据库查询之后的结果也放入缓存中。
- SQL 语句的执行经过了层层转化,依次经过了 MappedStatement 对象、Statement对象和 PreparedStatement对象,最后才得以执行。
- 最终数据库查询得到的结果交给 ResultHandler对象处理。