Mybatis整体工作流程介绍
一个标准的mybatis
查询通常如下所示,此处不考虑整合spring
,总体思想是类似的
1 | @Test |
- 获取
Mybatis
的配置文件,内部通过ClassLoader
加载文件流,这一步需要对Classloader
有一定的理解 - 创建
SqlSessionFactory
, 通过JDK内部的w3c解析配置文件的内容,封装到Configration
对象中,最后通过Configuration
来创建DefaultSqlSessionFactory
. - 通过
SqlSessionFactory
创建SqlSession
对象 - 不同的
executor
内部的查询方法不同,分为BatchExecutor
,ReuseExecutor
,SimpleExecutor
以及CachingExecutor
executor
的query
方法将真正的查询交给具体实现类的doQuery
来执行doquery
中会使用到的StatementHandler
用于封装处理jdbc
的statement
,ResultHandler
用于处理结果集。最后将结果返回为一个List<Object>
,selectOne
调用的还是SelectList
,只是在取结果集的时候,返回了第一个元素。
工作流程图:
源码体现方式
openSession:
1 | //打开对应数据源的Session |
获取Executor
1 | //根据executorType创建执行器 |
相关Handler
1 | public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { |
pluginAll
1 | //拦截器链,该类维护了一个实现Interceptor的集合,调用pluginAll时,会依次调用对应的拦截器。 |
插件(拦截器)开发
说是插件,其实就是类似拦截器一般的功能。在Mybatis
中,可以插入拦截器的地方有以下几个:
- executor (拦截执行器)
- parameterHandler (拦截参数)
- ResultSetHandler (拦截结果集)
- StatementHandler (拦截sql构建)
拦截位置 | 拦截内容 |
---|---|
Executor | query、update、flushStatements、commit、rollback、getTransaction、close、isClosed |
ParameterHandler | getParameterObject、setParameters |
ResultSetHandler | handleResultSets、handleCursorResultSets、handleOutputParameters |
StatementHandler | prepare、parameterize、batch、update、query |
在之前的newExecutor
方法,以及各种handler
处理器的地方提到过PluginAll
方法,其实就是对应的这几个位置。
接口介绍
Mybatis插件通过实现拦截器接口Interceptor来完成,原接口如下:
1 | public interface Interceptor { |
setProperties
主要是给拦截器提供参数用的,使用方式简单,此处不再介绍。
再来看plugin
方法,其参数为target
,即拦截器所要拦截的对象。前面说到InterceptorChain
维护了一个Interceptor
的集合,这里的plugin
方法实际是在对应的拦截位置,由InterceptorChain
进行循环调用时触发。实现类直接通过如下方式使用:
1 | @Override |
plugin.warp()
方法会自动判断拦截器的签名(接下来会介绍到)和被拦截的接口是否匹配,在两者一致的情况下会通过动态代理拦截该对象(如拦截器的签名为query
,那么在调用query
方法时会被拦截)。
intercept
方法则是拦截器执行拦截逻辑的地方,其参数类型为Invocation
,可以从中获取到很多反射相关的信息,如:
1 | @Override |
拦截器签名
在自定义拦截器的过程中,实现Interceptor
只表示声明了一个拦截器,但该拦截器实际在什么位置使用则需要拦截器签名来进行定义。
使用@Intercepts
和签名注解@Signature
来配置拦截器所要拦截的方法。
@Intercepts
注解中的属性是一个@Signature
签名数组,可以在同一个拦截器中同时拦截不同的接口和方法,使用方式如下:
1 | //以拦截参数处理器ParameterHandler的setParameters为例 |
@Signature
包含三个属性:
- type:设置拦截的接口,即
Executor
,ParameterHandler
,ResultSetHandler
,StatementHandler
四者中的一个 - method:设置拦截接口中的方法名,根据前面表格中的对应关系来。
- args:设置拦截方法的参数类型数组,通过方法名和参数类型可以确定唯一一个方法。
参考刘增辉老师的《Mybatis从入门到精通》
,接下来例举一些较为常用的被拦截方法和接口
(可先看后面的实例,回头再来理解各接口的拦截签名)
拦截Executor接口
Executor
接口包含的几个方法:
- int update(MappedStatedment ms,Object Parameter) throws SQLException
该方法会拦截所有的
INSERT、UPDATE、DELETE
操作,对应的签名为:
1 | @Signature(type = Executor.class, method = "update", args = {MappedStatedment.class,Object.class}) |
List query(MappedStatedment ms,Object parameter,RowBounds rowBounds,ResultHandler resultHandler) throws SQLException 该方法用于拦截所有的
SELECT
查询方法,一般是最常被拦截的方法,对应的签名为::1
@Signature(type = Executor.class, method = "query", args = {MappedStatedment.class,Object.class,RowBounds.class,ResultHandler.class})
void commit(boolean required) throws SQLException
该方法只在通过
sqlsession
调用commit
方法时才被调用,接口方法对应的签名为:1
@Signature(type = Executor.class, method = "commit", args = {boolean.class})
void rollback(boolean required) throws SQLException
该方法只在通过
sqlsession
调用rollback
方法时才被调用,接口方法对应的签名为:1
@Signature(type = Executor.class, method = "rollback", args = {boolean.class})
除以上之外,还有getTransaction
、isClosed
、close
、flushStatements
、queryCursor
等方法可以拦截,但是即应用不常见,此处略过。
拦截ParameterHandler接口
ParameterHandler
接口的方法很少,只有以下两个
Object getParameterObject()
该方法只在执行存储过程处理出参的时候被调用,接口对应的签名如下:
1
@Signature(type = ParameterHandler.class, method = "getParameterObject", args = {})
void setParameters(PreparedStatement var1) throws SQLException
该方法在设置SQL参数时被调用,接口对应的签名如下:
1
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatedment.class})
拦截ResultSetHandler
ResultSetHandler
接口包含如下三个方法:
List handleResultSets(Statement var1) throws SQLException 该方法会拦截除存储过程及返回值类型为
Cursor<T>
以外的查询方法,对应的签名为:1
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
Cursor handleCursorResultSets(Statement var1) throws SQLException 3.4.0新增方法,拦截返回值类型为
Cursor<T>
的方法,对应的签名为:1
@Signature(type = ResultSetHandler.class, method = "handleCursorResultSets", args = {Statement.class})
void handleOutputParameters(CallableStatement var1) throws SQLException
该方法在使用存储过程处理出参时被调用,对应的签名为:
1
@Signature(type = ResultSetHandler.class, method = "handleOutputParameters", args = {CallableStatement.class})
拦截StatementHandler接口
Statement prepare(Connection var1, Integer var2) throws SQLException
在数据库执行前被调用,优于当前接口中的其他方法,对应的签名为:
1
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class,Integer.class})
void parameterize(Statement var1) throws SQLException
在
prepare
方法之后执行,用于处理参数,对应的签名为:1
@Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class})
void batch(Statement var1) throws SQLException
在全局设置配置
defaultExecutorType="Batch"
时,操作数据库会执行该方法,对应的签名为:1
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
List query(Statement var1, ResultHandler var2) throws SQLException 执行
SELECT
方法时被调用,对应的签名为:1
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class,ResultHandler.class})
Cursor queryCursor(Statement var1) throws SQLException 3.4.0新增方法,在返回值类型为
Cursor<T>
的查询中被调用,对应的签名为:1
@Signature(type = StatementHandler.class, method = "queryCursor", args = {Statement.class})
拦截器实例
这里先介绍一个刘增辉老师书本上的例子,再介绍一个项目中实际使用到的场景。
下划线转驼峰插件
1 | /** |
项目中所用到的例子
拦截Executor
接口的update
和query
方法,对添加了自定义注解@DefaultParamsInsert
的方法方法进行默认参数追加。
(这里的作用类似于新增一条记录时。默认加上录入人id、行政区划、单位等)
1 | @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), |
以上です。