环境配置,包含id、TransactionFactory(事务工厂)、DataSource
TransactionFactory有三个实现类,我们与spring整合,默认使用第三个事务工厂
别名映射
全限定名:parameterType="java.lang.Integer"
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
别名:parameterType="integer"
<select id="selectByPrimaryKey" parameterType="integer" resultMap="BaseResultMap">
jdbc与java的类型映射处理器
比如StringTypeHandler,映射java的String,对应数据库VARCHAR
抽象类
字段包括:Configuration、TypeAliasRegistry、TypeHandlerRegistry
mapper构造器,一个mapper对应一个XMLMapperBuilder类,继承父类BaseBuilder
变量包括XPathParser、MapperBuilderAssistant、sqlFragments、mapper文件resource
在构造方法中,初始化了XPathParser、MapperBuilderAssistant(mapper缓存处理类)
mapper构造器助手,主要用来处理二级缓存的逻辑、构造MappedStatement对象,继承父类BaseBuilder
变量包括:当前namespace、文件resource、Cache(当前二级缓存)、unresolvedCacheRef(二级缓存CacheRef),其它属性是通用配置
变量包括:当前namespace、文件resource、当前二级缓存、unresolvedCacheRef,其它属性是通用配置
Statment构造器,用于解析继承父类BaseBuilder
xml中表达式构造器,用于添加动态sql对应的处理器、生成SqlSource对象
这里是引用
基于sax解析xml并封装document对象
基于jdk动态代理,在构造方法中创建SqlSession的代理类,代理主要是为了统一处理sqlSession方法执行前后的事务功能
作为SqlSessionTemplate的内部类,提供了SqlSessionTemplate动态代理SqlSession的InvocationHandler实现
解析mapper文件资源
传入参数为XNode,由parser.evalNode(“/mapper”)方法返回结果
解析出来的集合内容如下,包含我们写的sql 原始内容
遍历XNode,通过XMLStatementBuilder.parseStatementNode()解析每一个XNode
通过XMLLanguageDriver.createSqlSource,返回SqlSource。核心由XMLScriptBuilder来代替实现
XMLScriptBuilder.parseScriptNode
<select id="normalList" resultType="org.hopehomi.cloud.entity.Orders" parameterType="integer">
select * from orders
<where>
id = #{count}
</where>
</select>
2. 接下来判断XNode是否包含动态sql标签
如果包含动态sql标签,封装成DynamicSqlSource并返回
通过建造者模式,构造出MappedStatement,缓存到Configuration中
key为MappedStatement的id(namespace + “.” + id)
通过namespace获取到mapper接口,执行configuration.addMapper(boundType);
由MapperRegistry来执行
添加Mapper注解映射,比如@Select、@Update
MapperAnnotationBuilder的构造方法
返回SqlSessionFactory的默认实现DefaultSqlSessionFactory
初始化SqlSessionTemplate
构造方法中创建SqlSession的代理对象
MP的配置注入,如configLocation(mybatis的config文件)、mapperLocations(mapper文件路径)、MybatisConfiguration、globalConfig等
MP的全局配置,如banner、MetaObjectHandler(元对象字段填充控制器)、IdentifierGenerator(主键生成器)等
实现了Mybatis的Interceptor接口,用于执行过程的拦截
在intercept方法中,会解析mybatis的入参invocation,将解析完的参数传入到各个InnerInterceptor扩展的查询/修改方法中
官方提供了几个拦截器,我们可以在最前面先添加我们自己的拦截器,在执行的时候会最高优先级执行,如果是作为根模块来封装,可以通过责任链模式来构造一个我们自己的拦截器链,业务项目继承封装的接口即可
AbstractMybatisInterceptor
public abstract class AbstractMybatisInterceptor implements InnerInterceptor {
/**
* 查询拦截
* @param executor
* @param ms
* @param parameter
* @param rowBounds
* @param resultHandler
* @param boundSql
* @param chain
* @return
* @throws SQLException
*/
public abstract boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException;
/**
* 修改拦截
* @param executor
* @param ms
* @param parameter
* @param chain
* @return
* @throws SQLException
*/
public abstract boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException;
public int priority() {
return 0;
}
}
RootMybatisInterceptor
public class RootMybatisInterceptor extends AbstractMybatisInterceptor {
private final AbstractMybatisInterceptor[] interceptors;
public RootMybatisInterceptor(AbstractMybatisInterceptor[] interceptorChain) {
this.interceptors = interceptorChain;
}
@Override
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
InterceptorChain interceptorChain = new InterceptorChain(interceptors);
return this.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, interceptorChain);
}
@Override
public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
InterceptorChain interceptorChain = new InterceptorChain(interceptors);
return this.updateIntercept(executor, ms, parameter, interceptorChain);
}
@Override
public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
}
@Override
public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
return chain.next(executor, ms, parameter, chain);
}
@Override
public int priority() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
InterceptorChain
public class InterceptorChain {
private final List<AbstractMybatisInterceptor> chains;
private final AtomicInteger index;
public InterceptorChain(AbstractMybatisInterceptor[] interceptorChains) {
List<AbstractMybatisInterceptor> chainList = Arrays.stream(interceptorChains).sorted(Comparator.comparingInt(AbstractMybatisInterceptor::priority)).collect(Collectors.toList());
chains = new ArrayList<>();
index = new AtomicInteger(0);
chains.addAll(chainList);
}
public boolean next(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
int curIndex = index.getAndIncrement();
if (curIndex == chains.size()) {
return true;
}
AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
return abstractMybatisInterceptor.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
}
public boolean next(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
int curIndex = index.getAndIncrement();
if (curIndex == chains.size()) {
return true;
}
AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
return abstractMybatisInterceptor.updateIntercept(executor, ms, parameter, chain);
}
public List<AbstractMybatisInterceptor> getChains() {
return Collections.unmodifiableList(chains);
}
}
WriteReadMybatisIntercepts读写分离插件
@Slf4j
public class WriteReadMybatisIntercepts extends AbstractMybatisInterceptor {
@Override
public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.READ);
return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
}
@Override
public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.WRITE);
return chain.next(executor, ms, parameter, chain);
}
}
创建MybatisSqlSessionFactoryBean,将DataSource、MybatisPlusProperties、MybatisConfiguration、GlobalConfig等参数设置到MybatisSqlSessionFactoryBean中,调用MybatisSqlSessionFactoryBean.getObject()来构建实例
拷贝自Mybatis的SqlSessionFactoryBean,基本内容和SqlSessionFactoryBean一致
基于MybatisSqlSessionFactoryBean,他是一个factoryBean
返回bean类型为SqlSessionFactory
先判断sqlSessionFactory是否有值,没有值会进行初始化
封装Mybatis的Configuration,数据来源自前面传入的成员变量,需要满足mybatis需要的配置数据
赋值targetConfiguration
初始化Environment,将SpringManagedTransactionFactory作为事务工厂
mapper文件解析,这段代码和mybatis原生一样
最终调用到MybatisMapperRegistry.addMapper方法
该方法与mybatis的MapperRegistry.addMapper区别是替换了一些类,大致是一样的
下图是mybatis的MapperRegistry
与mybatis源码不同的是,加入了拦截策略的初始化,创建CRUD的mapperStatement加入到缓存中
区别如下两图所示
由于我们自定义的Mapper继承了BaseMapper,在BaseMapper中有默认的增删改查方法
最终由MapperBuilderAssistant.addMappedStatement添加到mappedStatements中
mp会在这里初始化一些默认配置(如主键生成器),最后依然是调用myabtis的.build方法来构建并返回SqlSessionFactory的实现
这里myabtis直接返回了DefaultSqlSessionFactory
下图为mybatis的SqlSessionFactoryBuilder.build
MP
MybatisMapperAnnotationBuilder.parse();
与mybatis不同的是,在这里插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(type)(根据Mapper类上注解来初始化)
在后面插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method)(根据具体方法上注解来初始化)
mapper类上是否存在@InterceptorIgnore注解
方法上是否存在@InterceptorIgnore注解