后面开始分析sql执行的源码流程
也就是这一部分
一、factory.openSession()
重点关注configuration.newExecutor这个方法,获取事务处理器比较简单,就是获取一个jdbc的事务管理器。
- 这个方法通过传入的执行器类型来创建不同的执行器,有simple、batch、reuse等
- 如果支持缓存的话,会创建CachingExecutor执行器
执行完成之后最后生成了一个DefaultSqlSession,有关DefaultSqlSession,特别要注意一点,它是一个非线程安全的类。
二、获取mapper接口
�
关于knownMappers前面讲过:是一个map,里面存放的是mapper的类型和MapperProxyFactory类。
后面就是调用mapperProxyFactory.newInstance(sqlSession)进行mapper接口的实例化。
最终会拿着mapper接口、代理方法缓存 生成InvokeHandler(用来生成Jdk代理必须要的类,当调用被代理的方法的时候,会调用到InvokeHandler的invokde方法)
最终返回的代理对象就是下图所示:
三、处理查询操作
当我们调用这个查询方法的时候,最终会调用到userProxy的invoke方法
先看cachedInvoker方法,
最终会生成一个PlainMethodInvoker,这个类是MapperProxy的静态内部类
这里会调用到mapperMethod.execute方法, MapperMethod中,command存放的是sql的一些信息,method存放方法的一些信息
public class MapperMethod {// todo 存放sql的一些信息,其中name属性存放的是“包名、类名、方法名”private final SqlCommand command;// todo 方法的签名,主要存放方法的一些信息private final MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);}public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:// todo 处理查询操作if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {// todo 返回多行记录result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}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;}
在execute方法里面,会处理 sql 的 INSERT(mapper.xml 中的 insert 标签)、UPDATE(mapper.xml 中的 update 标签)、DELETE(mapper.xml 中的 delete 标签)、SELECT(mapper.xml 中的 select 标签)、FLUSH(针对 BatchExecutor 执行器,执行缓存的 Statement)等操作,这里我们仅关注 select 操作
接下来调用DefaultSqlSession.selectList方法来执行
这里会从configuration里面通过接口对应的MappedStatement,通过执行器调用执行,由于我们默认设置了一级缓存,所以会执行到CacheingExecutor.query方法
这个delegate.query最终调用的是SimpleExecutor.query方法
最终会执行到simpleExecutor.doQuery方法
四、总结
本文主要介绍了 mybatis 执行 sql 的流程,介绍的内容如下:
- mybatis 的 sql 执行操作方法在 SqlSession 中,SqlSession 是 mybatis 的执行入口
- XxxMapper 是一个接口,mybatis 基于 jdk 动态代理机制会生成一个代理对象,其 InvocationHandler(具体类为 MapperProxy)的 invoker(…) 方法会获取 mapper.xml 定义的 sql 并执行
- 执行 XxxMapper 方法时,实际调用的是 MapperProxy#invoker(…) 方法,整个方法的执行过程中,会获取 mapper.xml 中的 sql 语句,然后使用执行器(SimpleExecutor、ReuseExecutor、BatchExecutor 等)处理 sql 的执行
- mybatis 提供的 DefaultSqlSession 是非线程安全的,想要线程安全,可以使用 SqlSessionManager