Java中的sqlsession_mybatis - buildSqlSessionFactory()

buildSqlSessionFactory() 这个方法比较长, 干的事情也比较多. 包括一些别名, 插件, 类型处理器等的解析.

从主流程上来看, 最主要的其实是干了两件事:

1. 对 mapper.xml 文件进行解析

2. 使用 SqlSessionFactoryBuilder 创建 sqlSessionFactory

mapper.xml的扫描工作不在这个方法里, 但是放到这里来看, 会更加清晰一点.

1. mapperLocation 的解析

@Bean

@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throwsException {

SqlSessionFactoryBean factory= newSqlSessionFactoryBean();

......if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {

factory.setMapperLocations(this.properties.resolveMapperLocations());

}returnfactory.getObject();

}

在 SqlSessionFactory 的创建方法中, 执行了一个  this.properties.resolveMapperLocations() 方法.

publicResource[] resolveMapperLocations() {

ResourcePatternResolver resourceResolver= newPathMatchingResourcePatternResolver();

List resources = new ArrayList();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(newResource[resources.size()]);

}

PathMatchingResourcePatternResolver是org.springframework.core 里面的一个类, 是 spring 提供的.

可以对 mapperLocations = classpath:mapper/**Mapper.xml 进行解析, 并拿到匹配路径下的文件资源

此例中得到的结果是 SchoolMapper.xml 和 UserMapper.xml 文件的 Resource . 并将它们放入了 mapperLocations 属性中.

2. mapper.xml 文件的解析

protected SqlSessionFactory buildSqlSessionFactory() throwsIOException {......if (!isEmpty(this.mapperLocations)) {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;

}try{

XMLMapperBuilder xmlMapperBuilder= newXMLMapperBuilder(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 voidparse() {if (!configuration.isResourceLoaded(resource)) {

configurationElement(parser.evalNode("/mapper"));

configuration.addLoadedResource(resource);

bindMapperForNamespace();

}

parsePendingResultMaps();

parsePendingCacheRefs();

parsePendingStatements();

}

2.1  解析mapper.xml配置文件

private voidconfigurationElement(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方法:

313cc2d1f0f74506f47a20c8e3cc8283.png

这里注意一个方法:

public voidaddMappedStatement(MappedStatement ms) {

mappedStatements.put(ms.getId(), ms);

}

这里的id是怎么来的呢?

MapperBuilderAssistant.java

public String applyCurrentNamespace(String base, booleanisReference) {if (base == null) {return null;

}if(isReference) {//is it qualified with any namespace yet?

if (base.contains(".")) {returnbase;

}

}else{//is it qualified with this namespace yet?

if (base.startsWith(currentNamespace + ".")) {returnbase;

}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 voidbindMapperForNamespace() {

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

publicSqlSessionFactory build(Configuration config) {return newDefaultSqlSessionFactory(config);

}

创建中, 主要就是持有了 Configuration 的引用.

到这里, mapper.java(还未创建实例) , mapper.xml 都已经找到了.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值