mybatis - buildSqlSessionFactory()
buildSqlSessionFactory() 这个方法比较长, 干的事情也比较多. 包括一些别名, 插件, 类型处理器等的解析.
从主流程上来看, 最主要的其实是干了两件事:
1. 对 mapper.xml 文件进行解析
2. 使用 SqlSessionFactoryBuilder 创建 sqlSessionFactory mapper.xml的扫描工作不在这个方法里, 但是放到这里来看, 会更加清晰一点.
1. mapperLocation 的解析
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
......if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
} return factory.getObject();
}
在 SqlSessionFactory 的创建方法中, 执行了一个 this.properties.resolveMapperLocations() 方法.
public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (this.mapperLocations != null) {
for (String mapperLocation : this.mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
PathMatchingResourcePatternResolver 是org.springframework.core 里面的一个类, 是 spring 提供的.
可以对 mapperLocations = classpath:mapper/**Mapper.xml 进行解析, 并拿到匹配路径下的文件资源
此例中得到的结果是 SchoolMapper.xml 和 UserMapper.xml 文件的 Resource . 并将它们放入了 mapperLocations 属性中.
2. mapper.xml 文件的解析
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
......
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
} try {
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 this.sqlSessionFactoryBuilder.build(configuration);
}
这里的 this.mapperLocations 就是 SchoolMapper.xml 和 UserMapper.xml 的 Resource.
这里主要关注一下 parse() 方法.
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
2.1 解析mapper.xml配置文件
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
这个方法中, 对xml中的配置进行解析, 并使用 SqlSource 进行记录. 最终封装进 Configuration 的 MappedStatement mappedStatement 属性中
如例子中的 getById方法:
这里注意一个方法:
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
这里的id是怎么来的呢?
MapperBuilderAssistant.java
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) {
return null;
}
if (isReference) {
// is it qualified with any namespace yet?
if (base.contains(".")) {
return base;
}
} else {
// is it qualified with this namespace yet?
if (base.startsWith(currentNamespace + ".")) {
return base;
}
if (base.contains(".")) {
throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
}
}
return currentNamespace + "." + base;
}
一番解析之后, UserMapper.xml 的getById 的 id = "com.study.demo.mybatis.mapper.SchoolMapper.getById"
2.2 绑定
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);
}
}
}
}
从方法明上看, 此方法将 mapper 和 namespace 进行绑定.
在addMapper 方法中, 执行了一个重要方法: getSqlSourceFromAnnotations()
这个方法其实就是对 接口方法进行注解检测的.
此例中, 如果将 SchoolMapper.xml 中的配置删掉, 然后改写SchoolMapper.java 中的getById方法
@Select("select id, name from school where id = #{id}")
public School getById(@Param("id") Integer id);
那么就会在这里解析并赋值.
那如果我即配置了 mapper.xml 文件, 又配置了 @Select 注解的情况下, 到底是 mapper.xml 起作用还是 @Select 起作用呢?
1. 当然, 首先不可能都起作用, 这个是很明确的, 不然出现歧义的时候, 是咋个处理呢?
2. 在解析@Select 之前, 其实已经解析过 mapper.xml 了, 如果xml已经解析并且记录, 那么即使 @Select 解析失败, 那是不是起码还有一个可以使用呢?
事实上, 不能那样使用, 会报错的. 且会导致 mapper.xml 也用不上了. 程序启动也会中断, 不能继续进行.
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.study.demo.mybatis.mapper.SchoolMapper.getById
3. SqlSessionFactory创建
// SqlSessionFactoryBuilder.java
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
这里创建了一个默认的实现类 : DefaultSqlSessionFactory, 且持有了 Configuration 的引用.
到这里, mapper.java(还未创建实例) , mapper.xml 都已经找到了.
mybatis - buildSqlSessionFactory()的更多相关文章
- [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 ...
随机推荐
- python 删除git Jenkinsfile文件
背景:在做ci集成的发现分支超过100个之后,pipline activity列表中前期的分支会被隐藏,这导致master分支在活动视图中不可见 解决方案:删除历史分支的Jenkinsfile 分支太 ...
- linux 定时任务设置
创建一个mysqlbak.sh文件 #!/bin/sh #文件名时间 time=`date '+%y-%m-%d %H:%M:%S'` #mysql用户 user=root #mysql密码 pass ...
- 虚拟机win7 安装过程中遇到虚拟工具安装失败
如果遇见这样的问题 倘若你用的是这样的镜像:cn_windows_7_enterprise_x64_dvd_x15-70741.iso 换镜像源.换镜像源.换镜像源! 换成带sp1的iso镜像:cn_ ...
- range()用法
来源:http://www.cnblogs.com/wangwp/p/4535299.html 例子:http://www.cnblogs.com/hongten/p/hongten_python_r ...
- 怎么把apk文件部署在云服务器上
你服务器直接使用nginx,将请求映射到固定目录,把你的apk放到该目录.其他都不用干了.
- 解决wxpy获取不到指定群聊对象的情况
我们可以通过Bot.friends 以及Bot.groups 来获取到所有的好友以及聊天群,这里需要注意的是,聊天群需要保存到通讯录中,不然可能会出现找不到聊天群的情况. 摘自:http://www. ...
- 【音乐欣赏】《Heart Made of Stone》 - The Tech Thieves
曲名:Heart Made of Stone 作者:The Tech Thieves Yeah It's been years now and I wonder Is it over? Do you ...
- NEKO's Maze Game
NEKO#ΦωΦ has just got a new maze game on her PC! The game's main puzzle is a maze, in the forms of a ...
- Django教程(2)
from Django official document; Django 最初被设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发. from 编写你的第一个 Django 应用, ...
- plt/sns draw histgram
当使用如下代码保存使用 plt.savefig 保存生成的图片时,结果打开生成的图片却是一片空白. import matplotlib.pyplot as pltx = np.triu(ged) # ...