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

简介: 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。

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


相关文章
|
3月前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(User.java)
mybatis简单案例源码详细【注释全面】——实体层(User.java)
13 0
|
14天前
|
SQL XML Java
MyBatis初探:揭示初始化阶段的核心流程与内部机制
MyBatis初探:揭示初始化阶段的核心流程与内部机制
31 2
MyBatis初探:揭示初始化阶段的核心流程与内部机制
|
14天前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
27 1
深入源码:解密MyBatis数据源设计的精妙机制
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
13 0
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——测试层(UserMapperTest.java)
mybatis简单案例源码详细【注释全面】——测试层(UserMapperTest.java)
9 0
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Dao层映射文件(UserMapper.xml)【重要】
mybatis简单案例源码详细【注释全面】——Dao层映射文件(UserMapper.xml)【重要】
10 0
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Dao层接口(UserMapper.java)
mybatis简单案例源码详细【注释全面】——Dao层接口(UserMapper.java)
7 0
|
1月前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(Role.java)
mybatis简单案例源码详细【注释全面】——实体层(Role.java)
7 0
|
1月前
|
Java 关系型数据库 数据库连接
mybatis简单案例源码详细【注释全面】——前期准备
mybatis简单案例源码详细【注释全面】——前期准备
11 0