黄埔做网站,wordpress双语切换,毕设代做网站,北京网站建设排名浩森宇特文章目录 一、目标#xff1a;XML语句构建器二、设计#xff1a;XML语句构建器三、实现#xff1a;XML语句构建器3.0 引入依赖3.1 工程结构3.2 XML语句构建器关系图3.3 I/O资源扫描3.4 SQL源码3.4.1 SQL对象3.4.2 SQL源码接口3.4.3 原始SQL源码实现类3.4.4 静态SQL源码实现类… 文章目录 一、目标XML语句构建器二、设计XML语句构建器三、实现XML语句构建器3.0 引入依赖3.1 工程结构3.2 XML语句构建器关系图3.3 I/O资源扫描3.4 SQL源码3.4.1 SQL对象3.4.2 SQL源码接口3.4.3 原始SQL源码实现类3.4.4 静态SQL源码实现类 3.5 动态上下文3.6 SQL节点3.6.1 SQL节点接口3.6.2 混合SQL节点实现类3.6.3 静态文本SQL节点 3.7 脚本语言驱动3.7.1 脚本语言驱动接口3.7.2 XML语言驱动器3.7.3 脚本语言注册器 3.8 类型处理器3.8.1 类型处理器接口3.8.2 类型处理器注册机 3.9 记号处理器3.9.1 记号处理器接口3.9.2 普通记号解析器 3.10 参数表达式3.11 修改映射器语句和参数映射3.11.1 修改映射器语句3.11.2 修改参数映射 3.12 修改类型别名3.13 修改配置文件3.14 解析构建器3.14.1 修改构建器基类3.14.2 XML脚本构建器3.14.3 SQL源码构建器3.14.4 XML语言构建器3.14.5 XML映射构建器解耦映射解析3.14.6 XML配置构建器 3.15 DefaultSqlSession 调用调整 四、测试XML语句构建器五、总结XML语句构建器 一、目标XML语句构建器
Mybatis ORM 框架的核心结构逐步体现出来包括解析、绑定、映射、事务、执行、数据源等。着手处理 XML 解析问题。满足我们解析时一些参数的整合和处理 这部分的解析就是在 XMLConfigBuilder#mapperElement 方法中的操作。看上去实现了功能但是会让人感觉不够规整。 怎么才能设计的易于扩展又比较干净 将这部分解析的处理使用设计原则将流程和职责进行解耦并结合我们的当前诉求优先处理静态 SQL 内容后面再处理动态 SQL 和更多的参数类型的处理。
二、设计XML语句构建器 怎么使用设计原则将流程和职责进行解耦并处理静态SQL内容 参照设计原则对于 XML 信息的读取各个功能模块的流程上应该符合单一职责而每个具体的实现又得具备迪米特法则这样实现出来的功能才能具备良好的扩展性。通常这类代码也会看着很干净。在解析过程中所属解析的不同内容按照各自的职责类进行拆解和串联调用。 不把所有的解析都在一个循环中处理而是在整个解析过程中 引入 映射构建器 和 语句构建器按照不同的职责分别进行解析。 XMLMapperBuilder: 映射构建器XMLStatementBuilder: 语句构建器 同时在语句构建器中引入脚本语言驱动器默认实现的是 XMLLanguageDriver这个类来具体操作静态和动态 SQL 语句节点的解析。Mybatis 源码中使用 Ognl 的方式进行处理。对于的类是 DynamicContext。
三、实现XML语句构建器
3.0 引入依赖 pom.xml !-- https://mvnrepository.com/artifact/ognl/ognl --
dependencygroupIdognl/groupIdartifactIdognl/artifactIdversion3.3.2/version
/dependency3.1 工程结构
mybatis-step-08
|-src|-main| |-java| |-com.lino.mybatis| |-binding| | |-MapperMethod.java| | |-MapperProxy.java| | |-MapperProxyFactory.java| | |-MapperRegistry.java| |-builder| | |-xml| | | |-XMLConfigBuilder.java| | | |-XMLMapperBuilder.java| | | |-XMLStatementBuilder.java| | |-BaseBuilder.java| | |-ParameterExpression.java| | |-SqlSourceBuilder.java| | |-StaticSqlSource.java| |-datasource| | |-druid| | | |-DruidDataSourceFacroty.java| | |-pooled| | | |-PooledConnection.java| | | |-PooledDataSource.java| | | |-PooledDataSourceFacroty.java| | | |-PoolState.java| | |-unpooled| | | |-UnpooledDataSource.java| | | |-UnpooledDataSourceFacroty.java| | |-DataSourceFactory.java| |-executor| | |-resultset| | | |-DefaultResultSetHandler.java| | | |-ResultSetHandler.java| | |-statement| | | |-BaseStatementHandler.java| | | |-PreparedStatementHandler.java| | | |-SimpleStatementHandler.java| | | |-StatementHandler.java| | |-BaseExecutor.java| | |-Executor.java| | |-SimpleExecutor.java| |-io| | |-Resources.java| |-mapping| | |-BoundSql.java| | |-Environment.java| | |-MappedStatement.java| | |-ParameterMapping.java| | |-SqlCommandType.java| | |-SqlSource.java| |-parsing| | |-GenericTokenParser.java| | |-TokenHandler.java| |-reflection| | |-factory| | | |-DefaultObjectFactory.java| | | |-ObjectFactory.java| | |-invoker| | | |-GetFieldInvoker.java| | | |-Invoker.java| | | |-MethodInvoker.java| | | |-SetFieldInvoker.java| | |-property| | | |-PropertyNamer.java| | | |-PropertyTokenizer.java| | |-wrapper| | | |-BaseWrapper.java| | | |-BeanWrapper.java| | | |-CollectionWrapper.java| | | |-DefaultObjectWrapperFactory.java| | | |-MapWrapper.java| | | |-ObjectWrapper.java| | | |-ObjectWrapperFactory.java| | |-MetaClass.java| | |-MetaObject.java| | |-Reflector.java| | |-SystemMetaObject.java| |-scripting| | |-defaults| | | |-RawSqlSource.java| | |-xmltags| | | |-DynamicContext.java| | | |-MixedSqlNode.java| | | |-SqlNode.java| | | |-StaticTextSqlNode.java| | | |-XMLLanguageDriver.java| | | |-XMLScriptBuilder.java| | |-LanguageDriver.java| | |-LanguageDriverRegistry.java| |-session| | |-defaults| | | |-DefaultSqlSession.java| | | |-DefaultSqlSessionFactory.java| | |-Configuration.java| | |-ResultHandler.java| | |-SqlSession.java| | |-SqlSessionFactory.java| | |-SqlSessionFactoryBuilder.java| | |-TransactionIsolationLevel.java| |-transaction| | |-jdbc| | | |-JdbcTransaction.java| | | |-JdbcTransactionFactory.java| | |-Transaction.java| | |-TransactionFactory.java| |-type| | |-JdbcType.java| | |-TypeAliasRegistry.java| | |-TypeHandler.java| | |-TypeHandlerRegistry.java|-test|-java| |-com.lino.mybatis.test| |-dao| | |-IUserDao.java| |-po| | |-User.java| |-ApiTest.java|-resources|-mapper| |-User_Mapper.xml|-mybatis-config-datasource.xml3.2 XML语句构建器关系图 解耦原 XMLConfigBuilder 中对 XML 的解析扩展映射构建器、语句构建器处理 SQL 的提取和参数的包装整个核心流程图以 XMLConfigBuilder#mapperElement 为入口串联调用。在 XMLStatement#parseStatementNode 方法中解析配置语句提取参数类型、结果类型 select idqueryUserInfoById parameterTypejava.lang.Long resultTypecom.lino.mybatis.test.po.User SELECT id, userId, userName, userHead FROM user WHERE id #{id} /select这里使用到脚本语言驱动器今昔解析处理创建 SqlSource 语句信息。SqlSource 包含了 BoundSql。同时这里扩展了 ParameterMapping 作为参数包装传递类而不仅仅作为 Map 结构包装。因为通过这样的方式可以封装解析后的 javaType/jdbcType 信息。
3.3 I/O资源扫描 Resources.java package com.lino.mybatis.io;import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;/*** description: 通过类加载器获得resource的辅助类*/
public class Resources {public static Reader getResourceAsReader(String resource) throws IOException {return new InputStreamReader(getResourceAsStream(resource));}public static InputStream getResourceAsStream(String resource) throws IOException {ClassLoader[] classLoaders getClassLoaders();for (ClassLoader classLoader : classLoaders) {InputStream inputStream classLoader.getResourceAsStream(resource);if (null ! inputStream) {return inputStream;}}throw new IOException(Could not find resource resource);}private static ClassLoader[] getClassLoaders() {return new ClassLoader[]{ClassLoader.getSystemClassLoader(),Thread.currentThread().getContextClassLoader()};}/*** 获取类示例* param className 类名称* return 实例类*/public static Class? classForName(String className) throws ClassNotFoundException {return Class.forName(className);}
}将 getResourceAsStream 从 private 改为 public 。
3.4 SQL源码
3.4.1 SQL对象 BoundSql.java package com.lino.mybatis.mapping;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** description: 绑定的SQL是从SqlSource而来将动态内容都处理完成得到的SQL语句字符串其中包括?还有绑定的参数*/
public class BoundSql {private String sql;private ListParameterMapping parameterMappings;private Object parameterObject;private MapString, Object additionalParameters;private MetaObject metaParameters;public BoundSql(Configuration configuration, String sql, ListParameterMapping parameterMappings, Object parameterObject) {this.sql sql;this.parameterMappings parameterMappings;this.parameterObject parameterObject;this.additionalParameters new HashMap();this.metaParameters configuration.newMetaObject(additionalParameters);}public String getSql() {return sql;}public ListParameterMapping getParameterMappings() {return parameterMappings;}public Object getParameterObject() {return parameterObject;}public Object getAdditionalParameter(String name) {return metaParameters.getValue(name);}public void setAdditionalParameter(String name, Object value) {metaParameters.setValue(name, value);}public boolean hasAdditionalParameter(String name) {return metaParameters.hasGetter(name);}
}3.4.2 SQL源码接口 SqlSource.java package com.lino.mybatis.mapping;/*** description: SQL源码*/
public interface SqlSource {/*** 获取SQL源** param parameterObject 参数对象* return BoundSqlSQL源*/BoundSql getBoundSql(Object parameterObject);
}3.4.3 原始SQL源码实现类 RawSqlSource.java package com.lino.mybatis.scripting.defaults;import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.builder.SqlSourceBuilder;
import com.lino.mybatis.scripting.xmltags.DynamicContext;
import com.lino.mybatis.scripting.xmltags.SqlNode;
import com.lino.mybatis.session.Configuration;
import java.util.HashMap;/*** description: 原始SQL源码 比 DynamicSqlSource 动态SQL处理快*/
public class RawSqlSource implements SqlSource {private final SqlSource sqlSource;public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class? parameterType) {this(configuration, getSql(configuration, rootSqlNode), parameterType);}public RawSqlSource(Configuration configuration, String sql, Class? parameterType) {SqlSourceBuilder sqlSourceParser new SqlSourceBuilder(configuration);Class? clazz parameterType null ? Object.class : parameterType;sqlSource sqlSourceParser.parse(sql, clazz, new HashMap());}Overridepublic BoundSql getBoundSql(Object parameterObject) {return sqlSource.getBoundSql(parameterObject);}private static String getSql(Configuration configuration, SqlNode rootSqlNode) {DynamicContext context new DynamicContext(configuration, null);rootSqlNode.apply(context);return context.getSql();}
}3.4.4 静态SQL源码实现类 StaticSqlSource.java package com.lino.mybatis.builder;import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.ParameterMapping;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.session.Configuration;
import java.util.List;/*** description: 静态sql源码*/
public class StaticSqlSource implements SqlSource {private String sql;private ListParameterMapping parameterMappings;private Configuration configuration;public StaticSqlSource(Configuration configuration, String sql) {this(configuration, sql, null);}public StaticSqlSource(Configuration configuration, String sql, ListParameterMapping parameterMappings) {this.sql sql;this.parameterMappings parameterMappings;this.configuration configuration;}Overridepublic BoundSql getBoundSql(Object parameterObject) {return new BoundSql(configuration, sql, parameterMappings, parameterObject);}
}3.5 动态上下文 DynamicContext.java package com.lino.mybatis.scripting.xmltags;import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import java.util.HashMap;
import java.util.Map;/*** description: 动态上下文*/
public class DynamicContext {public static final String PARAMETER_OBJECT_KEY _parameter;public static final String DATABASE_ID_KEY _databaseId;static {// 定义属性-getter方法映射ContextMap到ContextAccessor的映射注册到ognl运行时// 参考http://commons.apache.org/proper/commons-ognl/developer-guide.htmlOgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());// 将传入的参数对象统一封装为ContextMap对象继承了HashMap对象// 然后Ognl运行时环境在动态计算sql语句时// 会按照ContextAccessor中描述的Map接口的方式来访问和读取ContextMap对象获取计算过程中需要的参数。// ContextMap对象内部可能封装了一个普通的POJO对象也可以是直接传递的Map对象当然从外部是看不出来的因为都是使用Map的接口来读取数据。}private final ContextMap bindings;private final StringBuilder sqlBuilder new StringBuilder();private int uniqueNumber;/*** 在DynamicContext的构造函数中根据传入的参数对象是否为Map类型有两个不同构造ContextMap的方式。* 而ContextMap作为一个继承了HashMap的对象作用就是用于统一参数的访问方式用Map接口方法来访问数据。* 具体来说当传入的参数对象不是Map类型时Mybatis会将传入的POJO对象用MetaObject对象来封装。* 当动态计算sql过程需要获取数据时用Map接口的get方法包装 MetaObject对象的取值过程。** param configuration 配置项* param parameterObject 参数对象*/public DynamicContext(Configuration configuration, Object parameterObject) {// 绝大多数调用的地方 parameterObject 为nullif (parameterObject ! null !(parameterObject instanceof Map)) {// 如果是map型 ?? 这句是 如果不是map型MetaObject metaObject configuration.newMetaObject(parameterObject);bindings new ContextMap(metaObject);} else {bindings new ContextMap(null);}bindings.put(PARAMETER_OBJECT_KEY, parameterObject);bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());}public MapString, Object getBindings() {return bindings;}public void bind(String name, Object value) {bindings.put(name, value);}public void appendSql(String sql) {sqlBuilder.append(sql);sqlBuilder.append( );}public String getSql() {return sqlBuilder.toString().trim();}public int getUniqueNumber() {return uniqueNumber;}/*** 上下文map 静态内部类*/static class ContextMap extends HashMapString, Object {private static final long serialVersionUID 2977601501966151582L;private MetaObject parameterMetaObject;public ContextMap(MetaObject parameterMetaObject) {this.parameterMetaObject parameterMetaObject;}Overridepublic Object get(Object key) {String strKey (String) key;// 先去map中找if (super.containsKey(strKey)) {return super.get(strKey);}// 如果没找到再用ognl表达式去取值// 如person[0].birthdate.yearif (parameterMetaObject ! null) {return parameterMetaObject.getValue(strKey);}return null;}}static class ContextAccessor implements PropertyAccessor {Overridepublic Object getProperty(Map context, Object target, Object name) throws OgnlException {Map map (Map) target;Object result map.get(name);if (result ! null) {return result;}Object parameterObject map.get(PARAMETER_OBJECT_KEY);if (parameterObject instanceof Map) {return ((Map) parameterObject).get(name);}return null;}Overridepublic void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {MapObject, Object map (MapObject, Object) target;map.put(name, value);}Overridepublic String getSourceAccessor(OgnlContext ognlContext, Object o, Object o1) {return null;}Overridepublic String getSourceSetter(OgnlContext ognlContext, Object o, Object o1) {return null;}}
}3.6 SQL节点
3.6.1 SQL节点接口 SqlNode.java package com.lino.mybatis.scripting.xmltags;/*** description: SQL 节点*/
public interface SqlNode {/*** 应用动态上下文** param context 动态上下文* return boolean*/boolean apply(DynamicContext context);
}3.6.2 混合SQL节点实现类 MixedSqlNode.java package com.lino.mybatis.scripting.xmltags;import java.util.List;/*** description: 混合SQL节点*/
public class MixedSqlNode implements SqlNode {/*** 组合模式拥有一个SqlNode的List*/private ListSqlNode contexts;public MixedSqlNode(ListSqlNode contexts) {this.contexts contexts;}Overridepublic boolean apply(DynamicContext context) {// 依次调用list里每个元素的applycontexts.forEach(node - node.apply(context));return true;}
}3.6.3 静态文本SQL节点 StaticTestNode.java package com.lino.mybatis.scripting.xmltags;/*** description: 静态文本SQL节点*/
public class StaticTextSqlNode implements SqlNode {private String text;public StaticTextSqlNode(String text) {this.text text;}Overridepublic boolean apply(DynamicContext context) {// 将文本加入context中context.appendSql(text);return true;}
}3.7 脚本语言驱动
获取默认语言驱动器并解析 SQL 操作。在 XMLSriptBuilder 中处理静态 SQL 和 动态 SQL
3.7.1 脚本语言驱动接口 LanguageDriver.java package com.lino.mybatis.scripting;import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;/*** description: 脚本语言驱动*/
public interface LanguageDriver {/*** 创建SQL源** param configuration 配置项* param script 元素* param parameterType 参数类型* return SqlSource SQL源码*/SqlSource createSqlSource(Configuration configuration, Element script, Class? parameterType);
}定义脚本语言驱动接口提供创建 SQL 信息的方法入参包括配置、元素、参数。它有3个实现类XMLLanguageDriver、RawLanguageDriver、VelocityLanguageDriver这里只实现了第一种实现。
3.7.2 XML语言驱动器 XMLLanguageDriver.java package com.lino.mybatis.scripting.xmltags;import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;/*** description: XML 语言驱动器*/
public class XMLLanguageDriver implements LanguageDriver {Overridepublic SqlSource createSqlSource(Configuration configuration, Element script, Class? parameterType) {// 用XML脚本构建器解析XMLScriptBuilder builder new XMLScriptBuilder(configuration, script, parameterType);return builder.parseScriptNode();}
}关于 XML 语言驱动器的实现较简单只是封装了对 XMLScriptBuilder 的调用处理。
3.7.3 脚本语言注册器 LanguageDriverRegistry.java package com.lino.mybatis.scripting;import java.util.HashMap;
import java.util.Map;/*** description: 脚本语言注册器*/
public class LanguageDriverRegistry {/*** 脚本语言Map*/private final MapClass?, LanguageDriver LANGUAGE_DRIVER_MAP new HashMap(16);private Class? defaultDriverClass null;public void register(Class? cls) {if (cls null) {throw new IllegalArgumentException(null is not a valid Language Driver);}if (!LanguageDriver.class.isAssignableFrom(cls)) {throw new RuntimeException(cls.getName() does not implements LanguageDriver.class.getName());}// 如果没注册过再去注册LanguageDriver driver LANGUAGE_DRIVER_MAP.get(cls);if (driver null) {try {// 单例模式即一个Class只有一个对应的LanguageDriverdriver (LanguageDriver) cls.newInstance();LANGUAGE_DRIVER_MAP.put(cls, driver);} catch (Exception e) {throw new RuntimeException(Failed to load language driver for cls.getName(), e);}}}public LanguageDriver getDriver(Class? cls) {return LANGUAGE_DRIVER_MAP.get(cls);}public LanguageDriver getDefaultDriver() {return getDriver(getDefaultDriverClass());}public Class? getDefaultDriverClass() {return defaultDriverClass;}/*** Configuration()有调用 默认为XMLLanguageDriver** param defaultDriverClass 默认驱动类型*/public void setDefaultDriverClass(Class? defaultDriverClass) {register(defaultDriverClass);this.defaultDriverClass defaultDriverClass;}
}3.8 类型处理器
3.8.1 类型处理器接口 TypeHandler.java package com.lino.mybatis.type;import java.sql.PreparedStatement;
import java.sql.SQLException;/*** description: 类型处理器*/
public interface TypeHandlerT {/*** 设置参数** param ps 预处理语言* param i 次数* param parameter 参数对象* param jdbcType JDBC类型* throws SQLException SQL异常*/void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
}3.8.2 类型处理器注册机 TypeHandlerRegistry.java package com.lino.mybatis.type;import java.lang.reflect.Type;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;/*** description: 类型处理器注册机*/
public final class TypeHandlerRegistry {private final MapJdbcType, TypeHandler? JDBC_TYPE_HANDLER_MAP new EnumMap(JdbcType.class);private final MapType, MapJdbcType, TypeHandler? TYPE_HANDLER_MAP new HashMap(16);private final MapClass?, TypeHandler? ALL_TYPE_HANDLER_MAP new HashMap(16);public TypeHandlerRegistry() {}private void register(Type javaType, JdbcType jdbcType, TypeHandler? handler) {if (null ! javaType) {MapJdbcType, TypeHandler? map TYPE_HANDLER_MAP.computeIfAbsent(javaType, k - new HashMap(16));map.put(jdbcType, handler);}ALL_TYPE_HANDLER_MAP.put(handler.getClass(), handler);}
}3.9 记号处理器
3.9.1 记号处理器接口 TokenHandler.java package com.lino.mybatis.parsing;/*** description: 记号处理器*/
public interface TokenHandler {/*** 处理记号** param content 内容* return String 处理结果*/String handleToken(String content);
}3.9.2 普通记号解析器 GenericTokenParser package com.lino.mybatis.parsing;/*** description: 普通记号解析器处理 #{} 和 ${} 参数*/
public class GenericTokenParser {/*** 开始记号*/private final String openToken;/*** 结束记号*/private final String closeToken;/*** 记号处理器*/private final TokenHandler handler;public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {this.openToken openToken;this.closeToken closeToken;this.handler handler;}public String parse(String text) {StringBuilder builder new StringBuilder();if (text ! null text.length() 0) {char[] src text.toCharArray();int offset 0;int start text.indexOf(openToken, offset);// #{favouriteSection,jdbcTypeVARCHAR}// 这里循环解析参数参考 GenericTokenParserTest 如果解析 ${first_name} ${initial} ${last_name} reporting.这样的字符串,里面有3个${}while (start -1) {// 判断一下 ${ 前面是否有反斜杠这个逻辑在老版的mybatis(3.1.0)中是没有的if (start 0 src[start - 1] \\) {// 新版已经没有调用substring了改为调用offset方式提高效率builder.append(src, offset, start - offset - 1).append(openToken);offset start openToken.length();} else {int end text.indexOf(closeToken, start);if (end -1) {builder.append(src, offset,src.length - offset);offset src.length;} else {builder.append(src, offset, start - offset);offset start openToken.length();String context new String(src, offset, end - offset);// 得到一对大括号里的字符串后调用handler.handleToken比如替换变量builder.append(handler.handleToken(context));offset end closeToken.length();}}start text.indexOf(openToken, offset);}if (offset src.length) {builder.append(src, offset, src.length - offset);}}return builder.toString();}
}3.10 参数表达式 ParameterExpression.java package com.lino.mybatis.builder;import java.util.HashMap;/*** description: 参数表达式*/
public class ParameterExpression extends HashMapString, String {private static final long serialVersionUID -2417552199605158680L;public ParameterExpression(String expression) {parse(expression);}private void parse(String expression) {// #{property,javaTypeint,jdbcTypeNUMERIC}// 首先去除空白返回的p是第一个不是空白的字符位置int p skipWS(expression, 0);if (expression.charAt(p) () {// 处理表达式expression(expression, p 1);} else {// 处理属性property(expression, p);}}/*** 表达式可能是3.2的新功能** param expression 参数* param left 索引*/private void expression(String expression, int left) {int match 1;int right left 1;while (match 0) {if (expression.charAt(right) )) {match--;} else if (expression.charAt(right) () {match;}right;}put(expression, expression.substring(left, right - 1));jdbcTypeOpt(expression, right);}private void property(String expression, int left) {// #{property,javaTypeint,jdbcTypeNUMERIC}// property:VARCHARif (left expression.length()) {// 首先得到逗号或者冒号之前的字符串加入到propertyint right skipUntil(expression, left, ,:);put(property, trimmedStr(expression, left, right));// 第二处理javaTypejdbcTypejdbcTypeOpt(expression, right);}}private int skipWS(String expression, int p) {for (int i p; i expression.length(); i) {if (expression.charAt(i) 0x20) {return i;}}return expression.length();}private int skipUntil(String expression, int p, String endChars) {for (int i p; i expression.length(); i) {char c expression.charAt(i);if (endChars.indexOf(c) -1) {return i;}}return expression.length();}private void jdbcTypeOpt(String expression, int p) {// #{property,javaTypeint,jdbcTypeNUMERIC}// property:VARCHAR// 首先去除空白返回的p是第一个不是空白的字符位置p skipWS(expression, p);if (p expression.length()) {// 第一个property解析完有两种情况逗号和冒号if (expression.charAt(p) :) {jdbcType(expression, p 1);} else if (expression.charAt(p) ,) {option(expression, p 1);} else {throw new RuntimeException(Parsing error in { new String(expression) } in position p);}}}private void jdbcType(String expression, int p) {// property:VARCHARint left skipWS(expression, p);int right skipUntil(expression, left, ,);if (right left) {put(jdbcType, trimmedStr(expression, left, right));} else {throw new RuntimeException(Parsing error in { new String(expression) } in position p);}option(expression, right 1);}private void option(String expression, int p) {// #{property,javaTypeint,jdbcTypeNUMERIC}int left skipWS(expression, p);if (left expression.length()) {int right skipUntil(expression, left, );String name trimmedStr(expression, left, right);left right 1;right skipUntil(expression, left, ,);String value trimmedStr(expression, left, right);put(name, value);// 递归调用option进行逗号后面一个属性的解析option(expression, right 1);}}private String trimmedStr(String str, int start, int end) {while (str.charAt(start) 0x20) {start;}while (str.charAt(end - 1) 0x20) {end--;}return start end ? : str.substring(start, end);}
}3.11 修改映射器语句和参数映射
3.11.1 修改映射器语句 MappedStatement.java package com.lino.mybatis.mapping;import com.lino.mybatis.session.Configuration;/*** description: 映射器语句类*/
public class MappedStatement {private Configuration configuration;private String id;private SqlCommandType sqlCommandType;private SqlSource sqlSource;Class? resultType;public MappedStatement() {}public static class Builder {private MappedStatement mappedStatement new MappedStatement();public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, SqlSource sqlSource, Class? resultType) {mappedStatement.configuration configuration;mappedStatement.id id;mappedStatement.sqlCommandType sqlCommandType;mappedStatement.sqlSource sqlSource;mappedStatement.resultType resultType;}public MappedStatement build() {assert mappedStatement.configuration ! null;assert mappedStatement.id ! null;return mappedStatement;}}public Configuration getConfiguration() {return configuration;}public String getId() {return id;}public SqlCommandType getSqlCommandType() {return sqlCommandType;}public SqlSource getSqlSource() {return sqlSource;}public Class? getResultType() {return resultType;}
}修改 BoundSql 为 SqlSource。
3.11.2 修改参数映射 ParameterMapping.java package com.lino.mybatis.mapping;import cn.hutool.db.meta.JdbcType;
import com.lino.mybatis.session.Configuration;/*** description: 参数映射 #{property,javaTypeint,jdbcTypeNUMERIC}*/
public class ParameterMapping {private Configuration configuration;/*** property*/private String property;/*** javaType int*/private Class? javaType Object.class;/*** javaType NUMERIC*/private JdbcType jdbcType;private ParameterMapping() {}public static class Builder {private ParameterMapping parameterMapping new ParameterMapping();public Builder(Configuration configuration, String property, Class? javaType) {parameterMapping.configuration configuration;parameterMapping.property property;parameterMapping.javaType javaType;}public Builder javaType(Class? javaType) {parameterMapping.javaType javaType;return this;}public Builder jdbcType(JdbcType jdbcType) {parameterMapping.jdbcType jdbcType;return this;}public ParameterMapping build() {return parameterMapping;}}public Configuration getConfiguration() {return configuration;}public String getProperty() {return property;}public Class? getJavaType() {return javaType;}public JdbcType getJdbcType() {return jdbcType;}
}3.12 修改类型别名 TypeAliasRegistry.java package com.lino.mybatis.type;import com.lino.mybatis.io.Resources;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;/*** description: 类型别名注册机*/
public class TypeAliasRegistry {private final MapString, Class? TYPE_ALIASES new HashMap();public TypeAliasRegistry() {// 构造函数里注册系统内置的类型别名registerAlias(string, String.class);// 基本包装类型registerAlias(byte, Byte.class);registerAlias(long, Long.class);registerAlias(short, Short.class);registerAlias(int, Integer.class);registerAlias(integer, Integer.class);registerAlias(double, Double.class);registerAlias(float, Float.class);registerAlias(boolean, Boolean.class);}public void registerAlias(String alias, Class? value) {String key alias.toLowerCase(Locale.ENGLISH);TYPE_ALIASES.put(key, value);}public T ClassT resolveAlias(String string) {try {if (string null) {return null;}String key string.toLowerCase(Locale.ENGLISH);ClassT value;if (TYPE_ALIASES.containsKey(key)) {value (ClassT) TYPE_ALIASES.get(key);} else {value (ClassT) Resources.classForName(string);}return value;} catch (ClassNotFoundException e) {throw new RuntimeException(Could not resolve type alias string . Cause: e, e);}}
}修改 resolveAlias 方法添加不同别名之间和异常信息的处理。
3.13 修改配置文件 Configuration.java package com.lino.mybatis.session;import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;/*** description: 配置项*/
public class Configuration {/*** 环境*/protected Environment environment;/*** 映射注册机*/protected MapperRegistry mapperRegistry new MapperRegistry(this);/*** 映射的语句存在Map里*/protected final MapString, MappedStatement mappedStatements new HashMap(16);/*** 类型别名注册机*/protected final TypeAliasRegistry typeAliasRegistry new TypeAliasRegistry();/*** 脚本语言注册器*/protected final LanguageDriverRegistry languageRegistry new LanguageDriverRegistry();/*** 类型处理器注册机*/protected final TypeHandlerRegistry typeHandlerRegistry new TypeHandlerRegistry();/*** 对象工厂*/protected ObjectFactory objectFactory new DefaultObjectFactory();/*** 对象包装工厂*/protected ObjectWrapperFactory objectWrapperFactory new DefaultObjectWrapperFactory();/*** 准备资源列表*/protected final SetString loadedResources new HashSet();/*** 数据库ID*/protected String databaseId;public Configuration() {typeAliasRegistry.registerAlias(JDBC, JdbcTransactionFactory.class);typeAliasRegistry.registerAlias(DRUID, DruidDataSourceFactory.class);typeAliasRegistry.registerAlias(UNPOOLED, UnpooledDataSourceFactory.class);typeAliasRegistry.registerAlias(POOLED, PooledDataSourceFactory.class);languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);}public void addMappers(String packageName) {mapperRegistry.addMappers(packageName);}public T void addMapper(ClassT type) {mapperRegistry.addMapper(type);}public T T getMapper(ClassT type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}public boolean hasMapper(Class? type) {return mapperRegistry.hasMapper(type);}public void addMappedStatement(MappedStatement ms) {mappedStatements.put(ms.getId(), ms);}public MappedStatement getMappedStatement(String id) {return mappedStatements.get(id);}public TypeAliasRegistry getTypeAliasRegistry() {return typeAliasRegistry;}public Environment getEnvironment() {return environment;}public void setEnvironment(Environment environment) {this.environment environment;}public String getDatabaseId() {return databaseId;}/*** 生产执行器** param transaction 事务* return 执行器*/public Executor newExecutor(Transaction transaction) {return new SimpleExecutor(this, transaction);}/*** 创建语句处理器** param executor 执行器* param mappedStatement 映射器语句类* param parameter 参数* param resultHandler 结果处理器* param boundSql SQL语句* return StatementHandler 语句处理器*/public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {return new PreparedStatementHandler(executor, mappedStatement, parameter, resultHandler, boundSql);}/*** 创建结果集处理器** param executor 执行器* param mappedStatement 映射器语句类* param boundSql SQL语句* return ResultSetHandler 结果集处理器*/public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {return new DefaultResultSetHandler(executor, mappedStatement, boundSql);}/*** 创建元对象** param object 原对象* return 元对象*/public MetaObject newMetaObject(Object object) {return MetaObject.forObject(object, objectFactory, objectWrapperFactory);}/*** 创建类型处理器注册机** return TypeHandlerRegistry 类型处理器注册机*/public TypeHandlerRegistry getTypeHandlerRegistry() {return typeHandlerRegistry;}/*** 是否包含资源** param resource 资源* return 是否*/public boolean isResourceLoaded(String resource) {return loadedResources.contains(resource);}/*** 添加资源** param resource 资源*/public void addLoadedResource(String resource) {loadedResources.add(resource);}/*** 获取脚本语言注册机** return languageRegistry 脚本语言注册机*/public LanguageDriverRegistry getLanguageRegistry() {return languageRegistry;}
}3.14 解析构建器
3.14.1 修改构建器基类 BaseBuilder.java package com.lino.mybatis.builder;import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;/*** description: 构建器的基类建造者模式*/
public class BaseBuilder {protected final Configuration configuration;protected final TypeAliasRegistry typeAliasRegistry;protected final TypeHandlerRegistry typeHandlerRegistry;public BaseBuilder(Configuration configuration) {this.configuration configuration;this.typeAliasRegistry this.configuration.getTypeAliasRegistry();this.typeHandlerRegistry this.configuration.getTypeHandlerRegistry();}public Configuration getConfiguration() {return configuration;}protected Class? resolveAlias(String alias) {return typeAliasRegistry.resolveAlias(alias);}
}添加类型处理器注册机 TypeHandlerRegistry。
3.14.2 XML脚本构建器 XMLScriptBuilder.java package com.lino.mybatis.scripting.xmltags;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.defaults.RawSqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
import java.util.ArrayList;
import java.util.List;/*** description: XML脚本构建器*/
public class XMLScriptBuilder extends BaseBuilder {private Element element;private boolean isDynamic;private Class? parameterType;public XMLScriptBuilder(Configuration configuration, Element element, Class? parameterType) {super(configuration);this.element element;this.parameterType parameterType;}public SqlSource parseScriptNode() {ListSqlNode contents parseDynamicTags(element);MixedSqlNode rootSqlNode new MixedSqlNode(contents);return new RawSqlSource(configuration, rootSqlNode, parameterType);}private ListSqlNode parseDynamicTags(Element element) {ListSqlNode contents new ArrayList();// element.getText 拿到 SQLString data element.getText();contents.add(new StaticTextSqlNode(data));return contents;}
}XMLScriptBuilder#parseScriptNode 解析 SQL 节点的处理方式主要是对 RawSqlSource 的包装处理。
3.14.3 SQL源码构建器 SqlSourceBuilder.java package com.lino.mybatis.builder;import com.lino.mybatis.mapping.ParameterMapping;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.parsing.GenericTokenParser;
import com.lino.mybatis.parsing.TokenHandler;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** description: SQL源码构建器*/
public class SqlSourceBuilder extends BaseBuilder {private static final String parameterProperties javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName;public SqlSourceBuilder(Configuration configuration) {super(configuration);}public SqlSource parse(String originalSql, Class? parameterType, MapString, Object additionalParameters) {ParameterMappingTokenHandler handler new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);GenericTokenParser parser new GenericTokenParser(#{, }, handler);String sql parser.parse(originalSql);// 返回静态SQLreturn new StaticSqlSource(configuration, sql, handler.getParameterMappings());}private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {private ListParameterMapping parameterMappings new ArrayList();private Class? parameterType;private MetaObject metaParameters;public ParameterMappingTokenHandler(Configuration configuration, Class? parameterType, MapString, Object additionalParameters) {super(configuration);this.parameterType parameterType;this.metaParameters configuration.newMetaObject(additionalParameters);}public ListParameterMapping getParameterMappings() {return parameterMappings;}Overridepublic String handleToken(String content) {parameterMappings.add(buildParameterMapping(content));return ?;}/*** 构建参数映射** param content 参数* return 参数映射*/private ParameterMapping buildParameterMapping(String content) {// 先解析参数映射就是转化成一个 HashMap | #{favouriteSection,jdbcTypeVARCHAR}MapString, String propertiesMap new ParameterExpression(content);String property propertiesMap.get(property);Class? propertyType parameterType;ParameterMapping.Builder builder new ParameterMapping.Builder(configuration, property, propertyType);return builder.build();}}
}在上一章中关于 BoundSql.parameterMappings 的参数就是来自于 ParameterMappingTokenHandler#buildParameterMapping 方法进行构建处理的。具体的 javaType、jdbcType 会体现到 ParameterExpression 参数表达式中完成解析操作。
3.14.4 XML语言构建器
XMLStatementBuilder 语句构建器主要解析 XML 中 select|insert|update|delete 中的语句。 XMLStatementBuilder.java package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.SqlCommandType;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
import java.util.Locale;/*** description: XML语言构建器*/
public class XMLStatementBuilder extends BaseBuilder {private String currentNamespace;private Element element;public XMLStatementBuilder(Configuration configuration, Element element, String currentNamespace) {super(configuration);this.element element;this.currentNamespace currentNamespace;}/*** 解析语句(select|insert|update|delete)* select* idselectPerson* parameterTypeint* parameterMapdeprecated* resultTypehashmap* resultMappersonResultMap* flushCachefalse* useCachetrue* timeout10000* fetchSize256* statementTypePREPARED* resultSetTypeFORWARD_ONLY* SELECT * FROM PERSON WHERE ID #{id}* /select*/public void parseStatementNode() {String id element.attributeValue(id);// 参数类型String parameterType element.attributeValue(parameterType);Class? parameterTypeClass resolveAlias(parameterType);// 结果类型String resultType element.attributeValue(resultType);Class? resultTypeClass resolveAlias(resultType);// 获取命令类型(select|insert|update|delete)String nodeName element.getName();SqlCommandType sqlCommandType SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));// 获取默认语言驱动器Class? langClass configuration.getLanguageRegistry().getDefaultDriverClass();LanguageDriver langDriver configuration.getLanguageRegistry().getDriver(langClass);SqlSource sqlSource langDriver.createSqlSource(configuration, element, parameterTypeClass);MappedStatement mappedStatement new MappedStatement.Builder(configuration, currentNamespace . id, sqlCommandType, sqlSource, resultTypeClass).build();// 添加解析 SQLconfiguration.addMappedStatement(mappedStatement);}
}这部分内容的解析就是从 XMLConfigBuilder 拆解出来关于 Mapper 语句解析的部分通过这样的解耦设计会让整个流程更加清晰。XMLStatementBuilder#parseStatementNode 方法是解析 SQL 语句节点的过程包括语句的ID、参数类型、结果类型、命令(select|insert|update|delete)以及使用语言驱动器处理和封装 SQL 信息。当解析完成后写入到 Confiuration 配置文件中的 MapString, MappedStatement 映射语句存放中。
3.14.5 XML映射构建器解耦映射解析 XMLMapperBuilder.java package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;/*** description: XML映射构建器*/
public class XMLMapperBuilder extends BaseBuilder {private Element element;private String resource;private String currentNamespace;public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource) throws DocumentException {this(new SAXReader().read(inputStream), configuration, resource);}public XMLMapperBuilder(Document document, Configuration configuration, String resource) {super(configuration);this.element document.getRootElement();this.resource resource;}/*** 解析** throws Exception 异常*/public void parse() throws Exception {// 如果当前资源没有加载过再加载防止重复加载if (!configuration.isResourceLoaded(resource)) {configurationElement(element);// 标记一下已经加载过了configuration.addLoadedResource(resource);// 绑定映射器到namespaceconfiguration.addMapper(Resources.classForName(currentNamespace));}}/*** 配置mapper元素* mapper namespaceorg.mybatis.example.BlogMapper* select idselectBlog parameterTypeint resultTypeBlog* select * from Blog where id #{id}* /select* /mapper** param element 元素*/private void configurationElement(Element element) {// 1.配置namespacecurrentNamespace element.attributeValue(namespace);if (.equals(currentNamespace)) {throw new RuntimeException(Mappers namespace cannot be empty);}// 2.配置select|insert|update|deletebuildStatementFromContext(element.elements(select));}/*** 配置select|insert|update|delete** param list 元素列表*/private void buildStatementFromContext(ListElement list) {for (Element element : list) {final XMLStatementBuilder statementBuilder new XMLStatementBuilder(configuration, element, currentNamespace);statementBuilder.parseStatementNode();}}
}在 XMLMapperBuilder#parse 的解析中主要体现在资源解析判断、Mapper 解析和绑定映射器。 configuration.isResourceLoaded 资源判断避免重复解析做了一个记录。configuration.addMapper 绑定映射器主要是把 namespace com.lino.mybatis.test.dao.IUserDao 绑定到 Mapper 上。 也就是注册到映射器注册机里。 configurationElement 方法调用的 buildStatementFromContext重在处理 XML 语句构建器。
3.14.6 XML配置构建器 XMLConfigBuilder.java package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.datasource.DataSourceFactory;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import javax.sql.DataSource;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;/*** description: XML配置构建器建造者模式集成BaseBuilder*/
public class XMLConfigBuilder extends BaseBuilder {private Element root;public XMLConfigBuilder(Reader reader) {// 1.调用父类初始化Configurationsuper(new Configuration());// 2.dom4j 处理xmlSAXReader saxReader new SAXReader();try {Document document saxReader.read(new InputSource(reader));root document.getRootElement();} catch (DocumentException e) {e.printStackTrace();}}/*** 解析配置类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器** return Configuration*/public Configuration parse() {try {// 环境environmentsElement(root.element(environments));// 解析映射器mapperElement(root.element(mappers));} catch (Exception e) {throw new RuntimeException(Error parsing SQL Mapper Configuration. Cause: e, e);}return configuration;}/*** environments defaultdevelopment* environment iddevelopment* transactionManager typeJDBC* property name... value.../* /transactionManager* dataSource typePOOLED* property namedriver value${driver}/* property nameurl value${url}/* property nameusername value${username}/* property namepassword value${password}/* /dataSource* /environment* /environments*/private void environmentsElement(Element context) throws Exception {String environment context.attributeValue(default);ListElement environmentList context.elements(environment);for (Element e : environmentList) {String id e.attributeValue(id);if (environment.equals(id)) {// 事务管理器TransactionFactory txFactory (TransactionFactory) typeAliasRegistry.resolveAlias(e.element(transactionManager).attributeValue(type)).newInstance();// 数据源Element dataSourceElement e.element(dataSource);DataSourceFactory dataSourceFactory (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue(type)).newInstance();ListElement propertyList dataSourceElement.elements(property);Properties props new Properties();for (Element property : propertyList) {props.setProperty(property.attributeValue(name), property.attributeValue(value));}dataSourceFactory.setProperties(props);DataSource dataSource dataSourceFactory.getDataSource();// 构建环境Environment.Builder environmentBuilder new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}/*** mappers* mapper resourceorg/mybatis/builder/AuthorMapper.xml/* mapper resourceorg/mybatis/builder/BlogMapper.xml/* mapper resourceorg/mybatis/builder/PostMapper.xml/* /mappers*/private void mapperElement(Element mappers) throws Exception {ListElement mapperList mappers.elements(mapper);for (Element e : mapperList) {String resource e.attributeValue(resource);InputStream inputStream Resources.getResourceAsStream(resource);// 在for循环里每个mapper都重新new一个XMLMapperBuilder来解析XMLMapperBuilder mapperParser new XMLMapperBuilder(inputStream, configuration, resource);mapperParser.parse();}}
}在 XMLConfigBuilder#mapperElement 中把原来的流程化的处理进行解耦 调用 XMLMapperBuilder#parse 方法进行解析处理。
3.15 DefaultSqlSession 调用调整 DefaultSqlSession.java package com.lino.mybatis.session.defaults;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.util.List;/*** description: 默认sqlSession实现类*/
public class DefaultSqlSession implements SqlSession {private Configuration configuration;private Executor executor;public DefaultSqlSession(Configuration configuration, Executor executor) {this.configuration configuration;this.executor executor;}Overridepublic T T selectOne(String statement) {return this.selectOne(statement, null);}Overridepublic T T selectOne(String statement, Object parameter) {MappedStatement ms configuration.getMappedStatement(statement);ListT list executor.query(ms, parameter, Executor.NO_RESULT_HANDLER, ms.getSqlSource().getBoundSql(parameter));return list.get(0);}Overridepublic T T getMapper(ClassT type) {return configuration.getMapper(type, this);}Overridepublic Configuration getConfiguration() {return configuration;}
}这里调整不大主要体现在获取 SQL 的操作上。ms.getSqlSource().getBoundSql(parameter) 。
四、测试XML语句构建器 ApiText.java Test
public void test_SqlSessionFactoryExecutor() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(mybatis-config-datasource.xml));SqlSession sqlSession sqlSessionFactory.openSession();// 2.获取映射器对象IUserDao userDao sqlSession.getMapper(IUserDao.class);// 3.测试验证User user userDao.queryUserInfoById(1L);logger.info(测试结果{}, JSON.toJSONString(user));
}测试结果 10:31:42.114 [main] INFO c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:31:43.207 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 331418503.
10:31:43.333 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果{id:1,userHead:1_04,userId:10001,userName:小灵哥}从测试结果和调试的截图可以看出我们的 XML 解析处理拆解后已经顺利支撑我们的使用。
五、总结XML语句构建器
将原来的 CRUD 的代理通过设计原则进行拆分和解耦运用不用的类来承担不同的职责完成整个功能的实现。包括映射构建器、语句构建器、源码构建器的综合使用以及对应的引用脚本语言驱动和脚本构建器解析处理我们的 XML 中的 SQL 语句。