mybatis-plus - buildSqlSessionFactory()
一. buildSqlSessionFactory()
mybatis-plus 同样的是调用 factory.getObject() 方法来进行 SqlSessionFactory 创建的. 然后调用 buildSqlSessionFactory() 方法:
protected SqlSessionFactory buildSqlSessionFactory() throws Exception { MybatisConfiguration configuration; // TODO 加载自定义 MybatisXmlConfigBuilder
MybatisXMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
// TODO 使用自定义配置
configuration = new MybatisConfiguration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
} if (this.globalConfig == null) {
this.globalConfig = GlobalConfigUtils.defaults();
}
if (this.globalConfig.getDbConfig() == null) {
this.globalConfig.setDbConfig(new GlobalConfig.DbConfig());
} // TODO 初始化 id-work 以及 打印骚东西
configuration.init(this.globalConfig); if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
} if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
} if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
} if (hasLength(this.typeAliasesPackage)) {
// TODO 支持自定义通配符
List<String> typeAliasPackageList = new ArrayList<>();
if (typeAliasesPackage.contains(StringPool.ASTERISK) && !typeAliasesPackage.contains(StringPool.COMMA) && !typeAliasesPackage.contains(StringPool.SEMICOLON)) {
String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(this.typeAliasesPackage);
if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
LOGGER.warn("Can't find class in '[" + this.typeAliasesPackage + "]' package. Please check your configuration.");
} else {
typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
}
} else {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String one : typeAliasPackageArray) {
if (one.contains(StringPool.ASTERISK)) {
String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(one);
if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
LOGGER.warn("Can't find class in '[" + one + "]' package. Please check your configuration.");
} else {
typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
}
} else {
typeAliasPackageList.add(one);
}
}
}
for (String packageToScan : typeAliasPackageList) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
} // TODO 自定义枚举类扫描处理
if (hasLength(this.typeEnumsPackage)) {
Set<Class> classes;
if (typeEnumsPackage.contains(StringPool.STAR) && !typeEnumsPackage.contains(StringPool.COMMA)
&& !typeEnumsPackage.contains(StringPool.SEMICOLON)) {
classes = PackageHelper.scanTypePackage(typeEnumsPackage);
if (classes.isEmpty()) {
LOGGER.warn("Can't find class in '[" + typeEnumsPackage + "]' package. Please check your configuration.");
}
} else {
String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Assert.notNull(typeEnumsPackageArray, "not find typeEnumsPackage:" + typeEnumsPackage);
classes = new HashSet<>();
for (String typePackage : typeEnumsPackageArray) {
Set<Class> scanTypePackage = PackageHelper.scanTypePackage(typePackage);
if (scanTypePackage.isEmpty()) {
LOGGER.warn("Can't find class in '[" + typePackage + "]' package. Please check your configuration.");
} else {
classes.addAll(PackageHelper.scanTypePackage(typePackage));
}
}
}
// 取得类型转换注册器
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
for (Class cls : classes) {
if (cls.isEnum()) {
if (IEnum.class.isAssignableFrom(cls)) {
// 接口方式
typeHandlerRegistry.register(cls, EnumTypeHandler.class);
} else {
// 注解方式
Class<?> clazz = dealEnumType(cls);
if (null != clazz) {
typeHandlerRegistry.register(cls, EnumAnnotationTypeHandler.class);
} else {
// 原生方式
registerOriginalEnumTypeHandler(typeHandlerRegistry, cls);
}
}
}
}
} if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
} if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
} if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
} if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
} if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
} if (this.cache != null) {
configuration.addCache(this.cache);
} if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
} if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
} configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); // TODO 设置元数据相关 如果用户没有配置 dbType 则自动获取
if (globalConfig.getDbConfig().getDbType() == DbType.OTHER) {
try (Connection connection = AopUtils.getTargetObject(this.dataSource).getConnection()) {
globalConfig.getDbConfig().setDbType(JdbcUtils.getDbType(connection.getMetaData().getURL()));
} catch (Exception e) {
throw ExceptionUtils.mpe("Error: GlobalConfigUtils setMetaData Fail ! Cause:" + e);
}
}
SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration); // TODO SqlRunner
SqlHelper.FACTORY = sqlSessionFactory; // TODO 设置全局参数属性 以及 缓存 sqlSessionFactory
globalConfig.signGlobalConfig(sqlSessionFactory); if (!isEmpty(this.mapperLocations)) {
if (globalConfig.isRefresh()) {
//TODO 设置自动刷新配置 减少配置
new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
2, true);
}
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
} try {
// TODO 这里也换了噢噢噢噢
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
} if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return sqlSessionFactory;
}
方法和mybatis中的一样, 非常的长.
this.sqlSessionFactoryBuilder.build(configuration) 这里返回的是 : return new DefaultSqlSessionFactory(config);
这里先只看 xmlMapperBuilder.parse() 方法, 因为这是主流程的方法, 同时, 他也是 mybatis 中的方法:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
configurationElement 是解析 mapper.xml 文件. 很明显, 这里拿不到通用mapper的那些方法对应的 sql 信息.
接着来 bindMapperForNamespace() 方法:
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
方法是mybatis中的方法, 唯一不同的是, 这里的 configuration 不是mybatis中的那个 Configuration了, 而是 MybatisConfiguration.
所以 configuration.addMapper(boundType) 调用的就是 MybatisConfiguration.addMapper().
//MybatisConfiguration.java
@Override
public <T> void addMapper(Class<T> type) {
//MybatisMapperRegistry
mybatisMapperRegistry.addMapper(type);
} @Override
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
// TODO 如果之前注入 直接返回
return;
// throw new BindingException("Type " + type +
// " is already known to the MybatisPlusMapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new PageMapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// TODO 自定义无 XML 注入
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
Mybatis 在这里使用的是 MapperAnnotationBuilder.
但是 MybatisMapperAnnotationBuilder 继承自 Mybatis 的MapperAnnotationBuilder, 而且把里面的属性拷贝了一份. 方法有部分修改.
接着看 MybatisMapperAnnotationBuilder.parse() 方法:
@Override
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
// TODO 注入 CURD 动态 SQL (应该在注解之前注入)
//--------------mybatis-plus 新增的部分----------
if (BaseMapper.class.isAssignableFrom(type)) {
GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}
//---------------end --------------------------
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
1. GlobalConfigUtils.getSqlInjector()
public static ISqlInjector getSqlInjector(Configuration configuration) {
// fix #140
GlobalConfig globalConfiguration = getGlobalConfig(configuration);
ISqlInjector sqlInjector = globalConfiguration.getSqlInjector();
if (sqlInjector == null) {
sqlInjector = new DefaultSqlInjector();
globalConfiguration.setSqlInjector(sqlInjector);
}
return sqlInjector;
}
这里就是从配置中获取自定义 ISqlInjector , 如mybatis-plus实例中的 CustomSqlInjector.
如果获取不到自定义的, 则使用默认的 DefaultSqlInjector
2. ISqlInjector.inspectInject()
//AbstractSqlInjector.java 抽象类, 实现 ISqlInjector 接口
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
List<AbstractMethod> methodList = this.getMethodList();
Assert.notEmpty(methodList, "No effective injection method was found.");
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass));
mapperRegistryCache.add(className);
/**
* 初始化 SQL 解析
*/
if (GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration()).isSqlParserCache()) {
SqlParserHelper.initSqlParserInfoCache(mapperClass);
}
}
}
这里的 this.getMethodList() 调用的就是 DefaultSqlInjector 的 getMethodList() 方法:
public class DefaultSqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
return Stream.of(
new Insert(),
new Delete(),
new DeleteByMap(),
new DeleteById(),
new DeleteBatchByIds(),
new Update(),
new UpdateById(),
new SelectById(),
new SelectBatchByIds(),
new SelectByMap(),
new SelectOne(),
new SelectCount(),
new SelectMaps(),
new SelectMapsPage(),
new SelectObjs(),
new SelectList(),
new SelectPage()
).collect(Collectors.toList());
}
}
可以看到, 它返回了一个 sql操作方法集合.
2.1 m.inject()
遍历 List<AbstractMethod> , 调用其 inject() 方法.
/**
* 注入自定义方法
*/
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
Class<?> modelClass = extractModelClass(mapperClass);
if (null != modelClass) {
/**
* 注入自定义方法
*/
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
injectMappedStatement(mapperClass, modelClass, tableInfo);
}
}
injectMappedStatement 是一个抽象方法, 留给子类实现的. 说白了, 就是
Insert, Delete, DeleteByMap 等实现的. 其实就是根据 实体类和配置, 解析生成 sql 操作语句.
具体细节, 留给后面再看吧.
mybatis-plus - buildSqlSessionFactory()的更多相关文章
- mybatis - buildSqlSessionFactory()
buildSqlSessionFactory() 这个方法比较长, 干的事情也比较多. 包括一些别名, 插件, 类型处理器等的解析. 从主流程上来看, 最主要的其实是干了两件事:1. 对 mapper ...
- [DEBUG]-[org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:431)] 一直在创建sqlsession工厂原因
今天在做开发项目的时候出现一直在控台输出创建sqlsessionfactory'这个代码, tomcat一直在控制台上输出这个内容无法停止下来,那么到底是什么原因呢, 我们可以在输出信息上看到有个wa ...
- mybatis笔记3 一些原理的理解
1,mybatis流程跟踪,原理理解 基本思路: 从SqlSessionFactory的初始化出发,观察资源的准备和环境的准备,以及实现持久层的一些过程: 进入SqlSessionFactoryBea ...
- MyBatis 配置文件头部换行异常
INFO - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory ...
- Exception mybatis 配置文件:<typeAlias alias="***" type="***"/> 重复配置
INFO - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory ...
- 使用XSD校验Mybatis的SqlMapper配置文件(1)
这篇文章以前面对SqlSessionFactoryBean的重构为基础,先简单回顾一下做了哪些操作: 新建SqlSessionFactoryBean,初始代码和mybatis-spring相同: 重构 ...
- 重构Mybatis与Spring集成的SqlSessionFactoryBean(2)
三.代码重构 1.先使用Eclipse把buildSqlSessionFactory()方法中众多的if换成小函数 protected SqlSessionFactory buildSqlSessio ...
- 重构Mybatis与Spring集成的SqlSessionFactoryBean(1)
一般来说,修改框架的源代码是极其有风险的,除非万不得已,否则不要去修改.但是今天却小心翼翼的重构了Mybatis官方提供的与Spring集成的SqlSessionFactoryBean类,一来是抱着试 ...
- Mybatis解析动态sql原理分析
前言 废话不多说,直接进入文章. 我们在使用mybatis的时候,会在xml中编写sql语句. 比如这段动态sql代码: <update id="update" parame ...
- mybatis Result Maps collection already contains value for com.ebways.dictionary.dao.impl.PtInfoDaoImpl.beanMap
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.conte ...
随机推荐
- SpringBoot(一) 入门介绍
SpringBoot 简介 springBoot 是 spring 团队伴随着 spring4.0 一同发布的框架,已然成为该团队的一个非常重要的项目.其作用是帮助我们简单迅速地创建一个独立的产品级别 ...
- R语言入门:向量初探
R语言主要用于统计,因此引入了向量这个概念将更好地进行统计计算,在其他无法引入向量的语言当中则会使用循环来计算一些大规模的数据,在R语言当中则不需要,下面我们来看看R语言当中向量的具体用法吧! 首先, ...
- mysql第八课
开启事务: START TRANSACTION; 提交事务: COMMIT; 回滚事务: ROLLBACK; 事务的概念:原子性,一致性,隔离性,持久性 READ UNCOMMITTED(读了未提交) ...
- Linux进程间通信-管道深入理解(转)
原文地址:https://www.linuxidc.com/Linux/2018-04/151680.htm Linux进程通信系列文章将详细介绍各种通信方式的机制和区别 1.进程间通信 每个进程各自 ...
- 再访JavaScript对象(原型链和闭包)
一:原型链简介 JavaScript通常被描述为基于原型的语言 (从继承机制的角度)- 为了提供继承,对象(注意:区别于实例)可以拥有一个原型对象,它充当一个模板对象,它继承了方法和属性.对象的原型对 ...
- Linux设置文件权限和归属
前言:在Linux文件系统的安全模型中,为系统中的文件(或目录)赋予了两个属性:访问权限和文件所有者,简称为“权限”和“归属”.其中,访问权限包括读取.写入.可执行三种基本类型,归属包括属主(拥有该文 ...
- memcached的安装、常用命令以及在实际开发中的案例
Memcached注意缺乏安全认证以及安全管制需要将Memcached服务器放置在防火墙(iptables)之后 Linux平台 (CentOS)安装Memcached 安装依赖yum -y inst ...
- GitKraken 快速配置 SSH Key
快速使用 GitKraken 配置SSH keys git是现在最流行的版本管理工具,应用范围非常广泛,推荐一款git的可视化工具,这款 工具特别方便 它的官方如下https://www.gitkra ...
- 3、MapReduce详解与源码分析
文章目录 1 Split阶段 2 Map阶段 2.1分区 2.2排序 3 Shuffle阶段 4 Reduce阶段 1 Split阶段 首先,接到hdf文件输入,在mapreduce中的ma ...
- 【15】ML项目流程与正交化
结构化机器学习项目 一 ML项目流程 1,确立目标(确定开发/测试集 + 唯一最优化指标) 确定开发/测试集:开发/测试集应尽可能接近将来应用场景中的数据. 划分数据集:开发集和测试集大小足以评估模型 ...