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。
如果本文任何错误,请批评指教,不胜感激 !