示例代码
InputStream inputStream = new FileInputStream(new File("src/main/resources/mybatis-config.xml"));
// 1.加载配置文件创建configuration对象,创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2.创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.获取Mapper接口动态代理
BaseTermMapper mapper = sqlSession.getMapper(BaseTermMapper.class);
// 4.动态代理回调sqlSession中的查询方法,sqlSession将查询方法转发给Executor,
// Executor基于JDBC访问数据库获取数据并通过反射将数据转换成POJO并返回
BaseTerm baseTerm = mapper.selectById(1);
System.out.println(baseTerm.getName());
sqlSession.close();
前面我们已经知道mapper调用的selectById方法会交由MapperProxy动态代理类的invoke方法中去处理,具体是交由到sqlSession,最终由excutor执行,接着我们具体看一下excutor的源码流程。
Excutor获取数据库数据的流程的主要核心类有:
BaseExecutor:抽象类,实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法;
SimpleExecutor:默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象;
ReuseExecutor:使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象;
BatchExecutor:实现批量执行多条SQL语句的能力;
另外还有几个核心的Handler类
StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;
ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性
ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
首先我们看一下Executor的创建过程,可以看到在第二步创建sqlSession连接的时候就已经创建了executor对象
接着我们看一下CachingExecutor.query方法
这里我已经将二级缓存的代码注释掉,所以debug直接走到从数据库查询的代码。
接着我们看一下创建StatementHandler的代码
可以看到创建了RoutingStatementHandler,RoutingStatementHandler:Excutor组件真正实例化的子类,使用静态代理模式,根据StatementType创建哪个具体实体类;
默认会创建PreparedStatementHandler对象
PreparedStatmentHandler :使用预编译PrepareStatement对象访问数据库
所以可以看到如下
接着我们看一下PreparedStatmentHandler的query方法
可以看到最终调用了JDBC的原生查询方法,然后让ResultSetHandler进一步处理,
ResultSetHandler将从数据库查询得到的结果按照映射配置文件的映射规则,映射成相应的结果集对象。
接下来我们看一下ResultSetHandler具体是怎么操作的
可以看到handleResultSet会继续调用handleRowValues方法
接着我们看一下getRowValue的具体代码
这样就将从数据库中查询出来的数据通过反射赋值到实体类中了,最后返回出去
这样,MyBatis的主流程就全部走完。