Mybatis源码系列1-Mybaits初始化(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Mybatis源码系列1-Mybaits初始化(下)

XMLStatementBuilder 除了按照约定,解析"select|insert|update|delete"对应的标签属性以及子标签外。最重要的是还会通过MapperBuilderAssistant(构建助手),把解析出来的信息 封装成一个MappedStatement  放入到Configuration.mappedStatements 缓存中

类XMLStatementBuilder
public class XMLStatementBuilder extends BaseBuilder {
    private MapperBuilderAssistant builderAssistant;//构建助手
    //解析"select|insert|update|delete"标签
    public void parseStatementNode() {
    String id = context.getStringAttribute("id");
        ...解析
     //助手辅助封装成MappedStatement  
     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}
类MapperBuilderAssistant
public class MapperBuilderAssistant extends BaseBuilder {
    public MappedStatement addMappedStatement(...){
         id = applyCurrentNamespace(id, false);//id的处理
         //创建一个MappedStatement对象封装每一个SQL模板信息
        MappedStatement statement = statementBuilder.build();
        configuration.addMappedStatement(statement);    
    }
}
//Configuration类
public class Configuration {
    //mappedStatements缓存
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
    //添加MappedStatement
    public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);//把MappedStatement 作为KEY
      }
}

值得一提是id的处理。虽然我们仅仅在select标签配置了id  = "selectUser",

<select id="selectUser" resultType="com.wqd.model.User">

但是在构建MappedStatement时 ,并不是把“selectUser”作为id, 而是经过applyCurrentNamespace方法进行处理。

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;
  }

我们可以看出,此方法会返回 namespacec.id  作为MappedStatement.id ,同时在作为Configuration.mappedStatements 缓存中MappedStatement的Key. 这就是为啥我们是通过namespace.id的形式来定位SQL的原因


最后解析的结果就是每一个SQL 模板都会创建一个MappedStatement对象(封装sql模板相关信息),放入到Configuration.mappedStatements 中。

这样我们就可以 通过namespace.id 定位到对应的SQL了。

<mapper namespace="com.wqd.dao.UserMapper">
   <select id="selectUser" resultType="com.wqd.model.User">
      select * from user where id= #{id}
   </select>
</mapper>
User user = sqlSession.selectOne("com.wqd.dao.UserMapper.selectUser", 1);


1.2.2 绑定Mapper到命名空间

这一步也非常重要,如果namerspacer配置为一个Mpper类的全限定名。那么就会以namespace对应的类的Class为KEY,以MapperProxyFactory 为value 放入到 Configuration.MapperRegistry.knownMappers缓存中。

bindMapperForNamespace();
private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        //根据命名空间名,查找类Class类
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);//如果找到,就把关联关系放到缓存中
        }
      }
    }
  }
//Configuration类
public class Configuration {
    //mapperRegistry缓存
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
   public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
    }
}
//Mapper注册器
public class MapperRegistry {
    public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
          //以namespace对应的Class类为KEY。新建一个MapperProxyFactory为Value 
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
}

当我们获取通过UserMapper获取Mapper时,MapperProxyFactory 会为我们创建一个代理类,来执行对应CURD操作。

这样我们就可以通过类名获取到Mapper#selectUser了。

<mapper namespace="com.wqd.dao.UserMapper">
   <select id="selectUser" resultType="com.wqd.model.User">
      select * from user where id= #{id}
   </select>
</mapper>
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 

config配置文件与mapper配置文件解析完成后。最终得到了一个Configuration对象。 而这个Configuration对象就是Mybatis 所需要的所有配置信息


2.创建SqlSessionFactory

SqlSessionFactoryBuilder 的第二步就是通过Configuration 对象创建一个默认的SqlSessionFactory工厂出来。

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

DefaultSqlSessionFactory 用于创建Sqlsession

Sqlsession sqlsession  = DefaultSqlSessionFactory.openSession()


总结


Mybatis初始化的过程,其就是Config配置文件,Mapper文件被解析, Configuration对象被创建的过程。

  • 所有的配置信息都包含在Configuration 这个大对象中。
  • 每一个SQL模板信息会被解析成一个MappedStatement对象。根据MappedStatement对象内的SQL模板信息,我们可以生成一类SQL。

如果本文任何错误,请批评指教,不胜感激 !


相关文章
|
1月前
|
XML Java 数据库连接
mybatis源码研究、搭建mybatis源码运行的环境
这篇文章详细介绍了如何搭建MyBatis源码运行的环境,包括创建Maven项目、导入源码、添加代码、Debug运行研究源码,并提供了解决常见问题的方法和链接到搭建好的环境。
mybatis源码研究、搭建mybatis源码运行的环境
|
1月前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。
|
1月前
|
供应链 前端开发 Java
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
该博客文章介绍了一个使用Mybatis、Layui、MVC和JSP技术栈开发的服装库存管理系统,包括注册登录、权限管理、用户和货号管理、库存管理等功能,并提供了源码下载链接。
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
|
1月前
|
缓存 Java 数据库连接
我要手撕mybatis源码
该文章深入分析了MyBatis框架的初始化和数据读写阶段的源码,详细阐述了MyBatis如何通过配置文件解析、建立数据库连接、映射接口绑定、动态代理、查询缓存和结果集处理等步骤实现ORM功能,以及与传统JDBC编程相比的优势。
我要手撕mybatis源码
|
4月前
|
SQL XML Java
MyBatis初探:揭示初始化阶段的核心流程与内部机制
MyBatis初探:揭示初始化阶段的核心流程与内部机制
50 2
MyBatis初探:揭示初始化阶段的核心流程与内部机制
|
4月前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
77 1
深入源码:解密MyBatis数据源设计的精妙机制
|
4月前
|
SQL 缓存 Java
|
4月前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
43 1
|
4月前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
4月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)