Mybatis系列八:SqlSource的创建

Mybatis系列八:SqlSource的创建

概述

Mybatis系列七:XMLStatementBuilder创建MappedStatement》一文【4.2 解析SQL语句】章节中,解析SQL节点得到的SqlSource有两种:
DynamicSqlSource SQL语句有${param0}(文本替换,可能是一段SQL)或者使用了if/where(运行时决定是否拼接SQL片段)等节点需要运行时才能解析出要执行的SQL。
RawSqlSource 使用了#{param1}占位符或者没有使用,就是一个完整SQL,不需要运行时解析得到SQL。

一、RawLanguageDriver

1.1 根绝节点创建SqlSource

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
SqlSource source = super.createSqlSource(configuration, script, parameterType);
checkIsNotDynamic(source);
return source;
}

1.2 根据脚本创建SqlSource

public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
SqlSource source = super.createSqlSource(configuration, script, parameterType);
checkIsNotDynamic(source);
return source;
}

1.3 使用RAW语言时不允许动态内容

private void checkIsNotDynamic(SqlSource source) {
if (!RawSqlSource.class.equals(source.getClass())) {
throw new BuilderException(“Dynamic content is not allowed when using RAW language”);
}
}

二、XMLLanguageDriver

2.1 SQL参数设置处理器

// 创建一个{@link ParameterHandler},该参数将实际参数传递给JDBC语句。
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}

2.2 根据节点创建SqlSource

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
**参考《Mybatis系列七:XMLStatementBuilder创建MappedStatement》【**四、解析SQL语句 XMLScriptBuilder】
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}

2.3 根据脚本创建SqlSource

public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
if (script.startsWith("

三、静态SQL(加载时确定的SQL) RawSqlSource

静态SqlSource。它比{@link DynamicSqlSource}更快,因为映射是在启动期间计算的。

3.1 构造器

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;
// 解析配置时解析SQL
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
}

3.2 获取SQL语句

private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
rootSqlNode.apply(context);
return context.getSql();
}

3.3 根据参数获取BoundSql

public BoundSql getBoundSql(Object parameterObject) {
return sqlSource.getBoundSql(parameterObject);
}

四、动态SQL(运行时确定的SQL) DynamicSqlSource

4.1 构造器

public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}

4.2 根据参数获取BoundSql

public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
// 运行时根据参数解析SQL
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}

五、替换占位符 SqlSourceBuilder

5.1 解析SQL语句

#{param0}中可使用的属性有:javaType、jdbcTyp、mode、numericScale、resultMap、typeHandler、
jdbcTypeName。
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
// 处理参数占位符#{param0}替换为?,并记录下参数名称
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 寻找占位符并调用ParameterMappingTokenHandler处理占位符
GenericTokenParser parser = new GenericTokenParser("#{", “}”, handler);
// #{param0}替换为?,占位符名称即param0封装为ParameterMapping
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

5.2 占位符处理 ParameterMappingTokenHandler

private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
// 按照占位符#{param0}出现的顺序记录下参数信息
private List parameterMappings = new ArrayList();
private Class<?> parameterType;
private MetaObject metaParameters;

public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
super(configuration);
this.parameterType = parameterType;
this.metaParameters = configuration.newMetaObject(additionalParameters);
}

public List getParameterMappings() {
return parameterMappings;
}

// 占位符#{param0}中的param0封装为ParameterMapping,并替换为?
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return “?”;
}

// 占位符#{param0}中的param0封装为ParameterMapping。#{param0}中可使用的属性有:javaType、jdbcTyp、mode、numericScale、resultMap、typeHandler、jdbcTypeName,需要都解析出来。
// #{param0, jdbcType=BIGINT, typeHandler=com.lemon.handler.DateForLongTypeHandler}
private ParameterMapping buildParameterMapping(String content) {
Map<String, String> propertiesMap = parseParameterMapping(content);
// 参数名即param0
String property = propertiesMap.get(“property”);
Class<?> propertyType;
// 解析param0的Java类型
if (metaParameters.hasGetter(property)) {
propertyType = metaParameters.getGetterType(property);
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
propertyType = parameterType;
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get(“jdbcType”))) {
propertyType = java.sql.ResultSet.class;
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
propertyType = Object.class;
} else {
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
if (metaClass.hasGetter(property)) {
propertyType = metaClass.getGetterType(property);
} else {
propertyType = Object.class;
}
}
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if (“javaType”.equals(name)) {
javaType = resolveClass(value);
builder.javaType(javaType);
} else if (“jdbcType”.equals(name)) {
builder.jdbcType(resolveJdbcType(value));
} else if (“mode”.equals(name)) {
builder.mode(resolveParameterMode(value));
} else if (“numericScale”.equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if (“resultMap”.equals(name)) {
builder.resultMapId(value);
} else if (“typeHandler”.equals(name)) {
typeHandlerAlias = value;
} else if (“jdbcTypeName”.equals(name)) {
builder.jdbcTypeName(value);
} else if (“property”.equals(name)) {
// Do Nothing
} else if (“expression”.equals(name)) {
// 尚不支持基于表达式的参数
throw new BuilderException(“Expression based parameters are not supported yet”);
} else {
throw new BuilderException(“An invalid property '” + name + “’ was found in mapping #{” + content + "}. Valid properties are " + parameterProperties);
}
}
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
}
return builder.build();
}

// 解析占位符#{param0}中的param0封装为ParameterMapping。#{param0}中可使用的属性有:javaType、jdbcTyp、mode、numericScale、resultMap、typeHandler、jdbcTypeName,需要都解析出来。

// #{param0, jdbcType=BIGINT, typeHandler=com.lemon.handler.DateForLongTypeHandler}
private Map<String, String> parseParameterMapping(String content) {
try {
return new ParameterExpression(content);
} catch (BuilderException ex) {
throw ex;
} catch (Exception ex) {
throw new BuilderException(“Parsing error was found in mapping #{” + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, …} ", ex);
}
}
}

六、StaticSqlSource

6.1 构造器

public StaticSqlSource(Configuration configuration, String sql, List parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}

6.2 创建BoundSql

public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值