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 ...
随机推荐
- .NET CORE(C#) WPF 抽屉式菜单
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF 抽屉式菜单 阅读导航 本文背景 代码实现 本文参考 源码 ...
- javaSE学习笔记(17)---锁
javaSE学习笔记(17)---锁 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率.本文旨在对锁相关源码(本文中的源码来自JDK 8).使用场景进行举例,为读 ...
- java 测试 (junit+ junit 断言 + postman)
实际开发中,除了开发,我想测试也是必不可少的一环吧.从简单的@Test .main 方法测试 到 页面测试 ,断言,postman. bug是无处不在,随时发生的事,高效率的调试.检测可以节省大量的开 ...
- Unity容器实现AOP面向切面编程
为什么要有AOP 需求总是变化的,比如经常会对一些方法后期增加日志.异常处理.权限.缓存.事务的处理,遇到这种情况我们往往只能修改类. 为了应对变化,我们常常使用设计模式解决,但是也有其局限性:设计模 ...
- .Net 程序代码混淆加密工具 ILProtector
我的项目中某一部分信息比较敏感,但是.Net程序反编译之后连注释都看得到.需要把exe保护起来,如:代码混淆之后再加壳. Bing到一款.Net混淆工具 ILProtector 作为资深工具党, ...
- 数据结构(集合)学习之Map(一)
集合 框架关系图: 补充:HashTable父类是Dictionary,不是AbstractMap. Map: Map(接口)和Collection都属于集合,但是Map不是Collection的子类 ...
- 在C#中使用RESTful API的几种好方法
什么是Restful API REST 即Representational State Transfer的缩写.直接翻译的意思是"表现层状态转化". 它是一种互联网应用程序的API ...
- Wannafly挑战赛13 zzf的好矩阵 题解 答案解释
Wannafly挑战赛13 zzf的好矩阵 题解 文章目录 Wannafly挑战赛13 zzf的好矩阵 题解 分析 结论1 结论2 结论3 C数组对应带子说明 空白长度论述 后续黑色长度论述 能&qu ...
- 修改Ubuntu的apt-get源为国内镜像源的方法
1.原文件备份 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 2.编辑源列表文件 sudo vim /etc/apt/sources. ...
- ESLint 使用指南
ESLint 官方文档 About 页面分 About 和 Philosophy 两个部分对 ESLint 做了介绍,简洁明了,值得一读. 借助 ESLint,可将 静态代码分析 和 问题代码协助修复 ...