起飞(
feí
)
1. 准备阶段
我们将SpringBoot项目启动起来的时候,它会为我们加载一些组件以实现MyBatis的执行
SqlSessionFactoryBuilder
:创建SqlSessionFactory,使用建造者模式SqlSessionFactory
:SqlSession的工厂,用于创建SqlSessionSqlSession
:Through this interface you can execute commands, get mappers and manage transactions,官方注释如上,通过它可以执行命令,接口中有selectOne,selectList等方法;获取mapper和管理事务MapperRegistry
:主要调用它的getMapper方法,获取需要的mapper,其中有一个Map<Class<?>, MapperProxyFactory<?>> knownMappers字段,通过这个map获取我们需要的MapperProxyFactory,从而创建MapperProxy对象,MapperProxy
:其中封装了三个字段,sqlSession、mapperInterface和methodCache,mapperInterface是我们在dao层写的接口
SqlSessionFactoryBuilder:作用就是创建一个构建器,一旦创建了SqlSessionFactory,它的任务就算完成了,可以回收。
SqlSessionFactory:作用是创建SqlSession,而SqlSession相当于JDBC的一个Connection对象,每次应用程序需要访问数据库,我们就要通过SqlSessionFactory创建一个SqlSession,所以SqlSessionFactory在整Mybatis整个生命周期中(每个数据库对应一个SqlSessionFactory,是单例产生的)。
Mapper:是一个接口,并没有实现类,它的作用是发送SQL,返回我们需要的结果,或者发送SQL修改数据库表,所以它存活于一个SqlSession内,是一个方法级别的东西。当SqlSession销毁的时候,Mapper也会销毁。
打断点!进来!
我们可以在MybatisAutoConfiguration
中看到,SpringBoot会为我们加载一个SqlSessionFactory
的Bean,其中的applyConfiguration方法
会为Factory加载配置
在方法的最后我们可以看到
getObject方法
,进去看看
这里可以看到在SqlSessionFactoryBean
中看到它会读取我们的xml配置,然后是一堆读取配置信息的废话,最后我们可以发现一个build
方法,接着向下看
这里我们可以很清楚的看到,创建的是一个
DefaultSqlSessionFactory
之后会使用SqlSessionFactory
,创建一个SqlSessionTemplate
它调用的是如下构造方法
有了SqlSession会调用他的
getMapper方法
,在这里我们可以看见需要在Configurantion中获取mapper,而且type就是我们在dao层创建的接口的Class对象
我们在这里需要特别注意,MapperRegistry
出现了!调用getMapper,获取mapper代理对象mapperProxy
其中knownMappers存储的都是我们的mapper接口和对应的mapperProxyFactory
我们具体看一下MapperProxy的构造方法
我们熟悉其中的字段,sqlSession是我们之前创建的SqlSessionTemplate,MapperInterface是我们创建的dao接口
到了这里,准备阶段就算完成了,画个图瞅瞅
2. 执行一个Dao中的方法
MapperMethod
: 其中封装了如下两个字段,其中command包括了方法名和方法类型(Select、Insert、Delete、Update),而MethodSignature包含返回值类型等信息
private final SqlCommand command;
private final MethodSignature method;
MappedStatement
: 包含很多重要字段信息,如resource(加载的xml资源),configuration(配置),resultMap(在mapper.xml文件中resultMap的对应关系),还有我们要使用的BoundSql,下文有图示
MappedStatement 详解BoundSql
:官方注释和其中字段如下
*An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
*The SQL may have SQL placeholders “?” and an list (ordered) of an parameter mappings
*with the additional information for each parameter (at least the property name of the input object to read the value from).
*Can also have additional parameters that are created by the dynamic language (for loops, bind…).
我们从中可以获取要执行的Sql语句,Sql语句中包含有?占位符和对应的参数映射,它还可以有通过动态语言创建的附加的参数
private final String sql; //sql语句
private final List<ParameterMapping> parameterMappings; //参数映射关系
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
ResultSetWrapper
: 其中存着对应的列名,列对应的jdbc类型和对应的Java类型
我们在这个方法里,进去!
这里调用MapperProxy的Invoke方法
之后,会执行
MapperMethod
的invoke方法
之后根据sql语句类型继续向下执行,我们这里是SELECT
接着向下跟
下边是
DefaultSqlSession
调用SelectOne方法
下面是第一次执行query方法
,正戏开始!
MappedStatement其他属性如下
下面这个doQuery方法是
SimpleExector
调用
这里我们重点看一下prepareStatement(handler, ms.getStatementLog())方法
,在这里它将我们传递的参数set进去了
跟进去!
根据我们传入的值是Integer,匹配到这个setNonNullParameter
的重载方法,调用setInt
将我们的参数set进去
之后的调用链还很长,就不都截出来了,其中我们注意一下这个rsw变量,其中存的东西有点儿意思,它有列名,列的类型和对应的Java类型
一直查询到后面,其中有一个
applyPropertyMappings方法
比较重要,这里它会进行一个循环,会对实体类进行赋值(其中ResultMap,指定的属性和列对应关系,在我们启动项目的时候,就在加载xml文件的时候,被创建好了)
其中metaObject中存我们最终获取到的实体类的值
metaObject具体属性如下
之后返回到getRowValue方法中,rowValue便是数据库中的行值
之后就是关闭资源,不断的向前返回
result
结果
还是画个图总结一下吧,不难
搞定!!!