1.查询前
也就是springboot启动时做的工作
- 实例化SqlSessionFactory
1.构建一个DefaultSqlSessionFactory
,主要作用就是维护Configuration
和查询时获取DefaultSqlSession
,然后通过DefaultSqlSession执行查询操作。
2.实例化的过程中会解析mapper.xml中的各种标签封装成xxxsqlSource,保存在Configuration的mappedstatelment的sqlSource属性中。
3.解析mapper.xml中的各种标签的过程中会对已经解析过的xml对应的mapper进行保存,保存在Configuration的mapperRegistry
的konwnMappers
中,key是接口全限定名,value是接口对应的MapperProxyFactory
类型,保存是为了实例化mapper接口时能获取到mapper及对应的MapperProxyFactory(作用是实例化时创建mapper接口的代理类MapperProxy)
4.mybatis-plus这种,不写xml的,会解析baseMapper中的方法,根据实体类信息等,生成sql。 - 实例化SqlSessionTemplate
1.构建一个SqlSessionTemplate
,用来在实例化mapper接口时获取mapper以及在执行查询时获取sqlsession。SqlSessionTemplate里维护DefaultSqlSessionFactory,比如获取Configuration就会通过SqlSessionTemplate获取DefaultSqlSessionFactory然后在获取Configuration,查询时获取和数据库关联的sqlsession,也是通过SqlSessionTemplate维护的DefaultSqlSessionFactory的opensession方法获取到的,类型是DefaultSqlSession
。 - 实例化mapper
扫描mapper文件变成BeanDefinition
(@Mapper和@MapperScan),变成BeanDefinition后会把BeanDefinition中的BeanClass属性设置为MapperFactoryBean
类型,以便在spring容器实例化对象时,对mapper接口也进行实例化,也就是生成对应的代理类MapperProxy,用以执行mapper的增删改查方法。
实例化完这三个对象,springboot就可以等待前端调用接口然后执行mapper方法进行增删改查了。
2.查询时
也就是前端调接口,然后调service,然后调mapper的方法时做的工作
- 解析传参
当通过servcie调用mapper接口的方法时,会调用代理对象MapperProxy
的invoke 方法。然后会调用MapperMethod
的invoke 方法。在MapperMethod的invoke 方法会调用MapperMethod的execute方法。在这个方法中会调用SqlSessionTemplate
的对应方法执行查询,在调用之前会进行方法参数解析,最终方法是ParamNameResolver
类的getNamedParams,得到一个map,key是参数名,value是参数值。 - 获取最终的sql
mapper.xml中的sql会在MybatisAutoConfiguration
中构建SqlSessionFactory
时得到解析,如果有where if之类的标签会被解析成DynamicSqlSource
,如果是普通的查询语句(select * from departments where department_id=#{depId}
)则会被解析成RawSqlSource
,这个属性会被存在configuration的mappedstatements
属性中,属性名称为sqlSource。然后执行查询时,会从sqlSource中拿到对应的原始sql,然后再进行解析,也就是把方法调用时的传参拼接到sql中以及拼接where if这种动态标签,最终得到完整的sql。方法就是对应的SqlSource类的getBoundsql
方法。这里DynamicSqlSource
类的getBoundsql方法也会调用RawSqlSource的getBoundSql方法。
3.查询后
也就是查询出结果后做的工作
- 解析返回值
关键类DefaultResultSetHandler
,基本逻辑都是在这个类实现的。
关键类ResultSetWrapper
,保存要映射的字段集合和查询出的数值的字节数组 - 1.
DefaultResultSetHandler
类的handleResultSets
方法,先拿到需要映射的字段集合,封装在ResultSetWrapper
中,然后再获取一个resultmap类型的集合,每个resultmap保存需要映射的类型,如果有resultmap标签则会封装到resultmappings属性中。 - 2.
DefaultResultSetHandler
类的handleResultSet
方法,调用handleRowValues
方法处理结果集放到multipleResults
中。 - 3.
DefaultResultSetHandler
类的handleRowValues
方法,分别处理嵌套映射和非嵌套映射。 - 4.非嵌套映射,
handleRowValuesForSimpleResultMap
方法,遍历映射每行数据,调用getRowValue
方法。没加resultmap或者resultmap中没做映射的字段调用applyAutomaticMappings
方法,resultmap中映射的字段调用applyPropertyMappings
方法。具体方法就是调用对应字段类型的typeHandler
从字节数组中拿到数据进行转换。所有对应字段值的字节数组在ResultSetWrapper
的resultset
属性中。 - 5.嵌套映射,
handleRowValuesForNestedResultMap
方法,遍历映射每行数据,调用重载的getRowValue
方法,没加resultmap或者resultmap中没做映射的字段调用applyAutomaticMappings
方法,resultmap中映射的字段调用applyPropertyMappings
方法,嵌套映射的字段调用applyNestedResultMappings
方法。而applyNestedResultMappings会再次调用getRowValue方法解析每行数据,逻辑和非嵌套映射相同。