excel表格如何保存为pdf
825
2022-05-29
前言
接上一篇,今天我们接着来分析MyBatis的源码。今天的分析的核心是SQL的执行过程。主要分为如下章节进行分析
代理类的生成
SQL的执行过程
处理查询结果
mapper 接口的代理类的生成过程分析
首先我们来看看mapper 接口的代理类的生成过程,如下是一个MyBatis查询的调用实例。
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List
1
2
上述方法sqlSession.getMapper(StudentMapper.class) 返回的其实是StudentMapper的代理类。
接着我们来看看调用的时序图。
如上时序图我们可知,接口的代理类(MapperProxy)最终由MapperProxyFactory通过JDK动态代理生成。接着我们一步步分析下。
//DefaultSqlSession @Override public
1
2
3
4
5
6
如上,DefaultSqlSession直接请求抛给Configuration。
//Configuration public
1
2
3
4
同样,Configuration也是一个甩手掌柜,将请求直接抛给了MapperRegistry 这个接盘侠。
接下来我们来看看接盘侠MapperRegistry。
//*MapperRegistry public
1
2
3
4
5
6
7
8
9
10
11
12
如上,在MapperRegistry的getMapper的方法中,首先根据配置的Mapper 获取其对应的MapperProxyFactory。接着调用newInstance方法返回MapperProxy。最后我们来看看MapperProxyFactory
//*MapperProxyFactory protected T newInstance(MapperProxy
1
2
3
4
5
6
7
8
9
10
如上,通过JDK自带的动态代理生成映射器,PS: JDK 动态代理需要接口。
分析完了MapperProxy的生成过程,接下来我们来分析下SQL的执行过程。
SQL的执行过程
SQL 的执行过程是从MapperProxy的invoke方法开始。按照惯例我们还是先看看相关的时序图。
如上图,在MapperProxy的invoke方法里调用了MapperMethod的execute方法,该方法是真正执行SQL,返回结果的方法。接下来我们来看看。
//*MapperProxy public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法 //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //这里优化了,去缓存中找MapperMethod final MapperMethod mapperMethod = cachedMapperMethod(method); //执行 return mapperMethod.execute(sqlSession, args); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如上,这里MyBatis做了个优化,如果缓存中有MapperMethod,则取缓存中的,如果没有则new一个MapperMethod实例。
//*MapperProxy //去缓存中找MapperMethod private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { //找不到才去new mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
1
2
3
4
5
6
7
8
9
10
11
我们接着来看看MapperMethod 中的execute方法,该方法主要是通过区分各种CURD操作(insert|update|delete|select),分别调用sqlSession中的4大类方法。源码如下:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法 if (SqlCommandType.INSERT == command.getType()) { // 对用户传入的参数进行转换,下同 Object param = method.convertArgsToSqlCommandParam(args); // 执行插入操作,rowCountResult方法用于处理返回值 result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } // 根据目标返回方法的返回类型进行相应的查询操作。 else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { /* 如果方法返回值为void,但参数列表中包含ResultHandler, 想通过ResultHandler的方式获取查询结果,而非通过返回值获取结果 * */ executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { //如果结果有多条记录 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { //如果结果是map result = executeForMap(sqlSession, args); } else { //否则就是一条记录 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { throw new BindingException("Unknown execution method for: " + command.getName()); } // 如果方法的返回值是基本类型,而返回值却为null,此种情况下应抛出异常 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
如上,代码注释比较详细。前面也说过了,不同的操作调用sqlSession中不同的方法。这里我重点分析下查询操作。查询的情况分为四种:
返回值为空
返回多条记录
返回map
返回单条记录。
返回值为空的情况下,直接返回 result 为null。其余几种情况内部都调用了sqlSession 中的selectList 方法。下面我就以返回单条记录为例进行分析。
//DefaultSqlSession public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
如果所示,如果selectList查询返回1条,则直接返回,如果返回多条则抛出异常,否则直接返回null。我们接着往下看.
//DefaultSqlSession public
1
2
3
4
5
6
7
8
9
10
11
12
13
如上,在selectList 内部最终调用的是SimpleExecutor (执行器)的query方法来执行查询结果。我们接着往下找
根据类图我们不难发现SimpleExecutor是BaseExecutor类的子类。在BaseExecutor 类中我们找到了query 方法。
public
1
2
3
4
5
6
7
8
该方法主要有两步,
得到绑定的SQL,
调用其重载query方法。
绑定SQL的过程,我们稍后分析。我们接着来看看其重载的query方法。
public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
该方法核心的步骤是,首先根据cacheKey 从localCache 中去查,如果不为空的话则直接取缓存的,否则查询数据库。我们主要看看查询数据库的queryFromDatabase方法。
//BaseExecutor //从数据库查 private
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
此处doQuery方法是抽象方法,定义了模板供子类实现。此处用到了模板模式。
首先,此方法首先调用doQuery方法执行查询,然后将查询的结果放入缓存中。
接着我们再来看看SimpleExcutor中的doQuery方法。
//*SimpleExcutor public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如上,该方法主要有三步:
新建一个StatementHandler
获取Statement
StatementHandler.query(实际调用的是PreparedStatementHandler)获取查询结果。
第一步比较简单,我们首先来看看第二步
//*SimpleExcutor private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 获取数据库连接 Connection connection = getConnection(statementLog); //创建Statement stmt = handler.prepare(connection); //为Statement设置IN参数 handler.parameterize(stmt); return stmt; }
1
2
3
4
5
6
7
8
9
10
11
对于prepareStatement方法里的相关步骤,相信大家都不会陌生。获取数据库连接,创建Statement; 为Statement设置IN参数。都是我们非常熟悉的。我们接着看看第三步。
public
1
2
3
4
5
6
7
这一步到了最终的执行链。还是先执行SQL,然后处理执行结果。限于篇幅,在此不展开分析了。
总结
本文通过两个时序图,为主线来展开分析了Mapper接口代理类的生成过程,以及SQL的执行过程。希望对大家有所帮助。
MyBatis SQL
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。