/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
/**
*
*
*
* @author Clinton Begin
*/
public interface SqlNode {
/**
* 将解析出来的值应用到context中
* @param context 一个用于记录动态SQL语句解析结果的容器
* @return 是否应用到 {@code context} 中
*/
boolean apply(DynamicContext context);
}
DynamicContext
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import ognl.OgnlContext;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
/**
* 主要用于记录解析动态SQL语句之后产生的SQL语句片段,可以认为它是一个用于记录动态SQL语句解析结果的容器
*
*
设置 OGNL的属性访问器,实现ContextMap和ContextAccessor
*
用StringJoiner sqlBuilder = new StringJoiner(" ")记录Sql,其实就是将之前解析的多个Sql片段进行合并。
*/
private final StringJoiner sqlBuilder = new StringJoiner(" ");
/**
* 唯一数字
*/
private int uniqueNumber = 0;
/**
*
* @param configuration mybatis全局配置信息
* @param parameterObject 参数对象
*/
public DynamicContext(Configuration configuration, Object parameterObject) {
//参数对象是Map时
if (parameterObject != null && !(parameterObject instanceof Map)) {
//构造parameterObject的元对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//是否存在对parameterObject的TypeHandler
boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
bindings = new ContextMap(metaObject, existsTypeHandler);
} else {
bindings = new ContextMap(null, false);
}
//添加parameteObject和databaseId到bindings
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}
/**
*
* @return {@see #bindings}
*/
public Map getBindings() {
return bindings;
}
/**
* 赋值给 {@see #bindings}
* @param name bindings.key
* @param value bindings.value
*/
public void bind(String name, Object value) {
bindings.put(name, value);
}
/**
* 添加SQL脚本到 {@link #sqlBuilder}
* @param sql sql脚本
*/
public void appendSql(String sql) {
sqlBuilder.add(sql);
}
/**
* 获取SQL脚本,去除了空格
* @return {@link #sqlBuilder}.toString().trim();
*/
public String getSql() {
return sqlBuilder.toString().trim();
}
/**
* 获取唯一数字,每获取一次 {@link #uniqueNumber} +1
* @return {@see #uniqueNumber}
*/
public int getUniqueNumber() {
return uniqueNumber++;
}
/**
* 上下文Map
*/
static class ContextMap extends HashMap {
private static final long serialVersionUID = 2977601501966151582L;
/**
* 参数元对象
*/
private final MetaObject parameterMetaObject;
/**
* 应变参数对象
*/
private final boolean fallbackParameterObject;
/**
*
* @param parameterMetaObject 参数元对象
* @param fallbackParameterObject 应变参数对象
*/
public ContextMap(MetaObject parameterMetaObject, boolean fallbackParameterObject) {
this.parameterMetaObject = parameterMetaObject;
this.fallbackParameterObject = fallbackParameterObject;
}
@Override
public Object get(Object key) {
String strKey = (String) key;
//如果key存在在容器中,直接返回
if (super.containsKey(strKey)) {
return super.get(strKey);
}
//参数元对象为null,直接返回
if (parameterMetaObject == null) {
return null;
}
//是应变参数对且没有在参数元对象像中找到对应strKey对应的Getter方法
if (fallbackParameterObject && !parameterMetaObject.hasGetter(strKey)) {
return parameterMetaObject.getOriginalObject();//返回参数元对象的原始对象
} else {
// issue #61 do not modify the context when reading 读取时不要修改上下文
//从参数元对象中获取值,支持strKey='order[0].item[0].name'的获取
return parameterMetaObject.getValue(strKey);
}
}
}
static class ContextAccessor implements PropertyAccessor {
@Override
public Object getProperty(Map context, Object target, Object name) {
//这里的map其实就是ContextMap
Map map = (Map) target;
//直接获取contextMap中的keyvalue或者获取MetaObject中的getValue
Object result = map.get(name);
if (map.containsKey(name) || result != null) {
return result;
}
//如参数对象本身是个map,直接获取
Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
if (parameterObject instanceof Map) {
return ((Map)parameterObject).get(name);
}
return null;
}
/**
* 设置属性
* @param context
* @param target 其实就是ContextMap实例
* @param name 传给ContextMap实例的key
* @param value 传给ContextMap实例的value
*/
@Override
public void setProperty(Map context, Object target, Object name, Object value) {
Map
ChooseSqlNode
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.List;
/**
* chooseSQL节点。
* @author Clinton Begin
*/
public class ChooseSqlNode implements SqlNode {
/**
* otherwise标签
*/
private final SqlNode defaultSqlNode;
/**
* when标签集合
*/
private final List ifSqlNodes;
/**
*
* @param ifSqlNodes when标签集合
* @param defaultSqlNode otherwise标签
*/
public ChooseSqlNode(List ifSqlNodes, SqlNode defaultSqlNode) {
this.ifSqlNodes = ifSqlNodes;
this.defaultSqlNode = defaultSqlNode;
}
@Override
public boolean apply(DynamicContext context) {
// when 节点根据 test 表达式判断是否生效
for (SqlNode sqlNode : ifSqlNodes) {
if (sqlNode.apply(context)) {
return true;
}
}
// when 节点如果都未生效,且存在 otherwise 节点,则使用 otherwise 节点
if (defaultSqlNode != null) {
defaultSqlNode.apply(context);
return true;
}
return false;
}
}
ForEachSqlNode
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.Map;
import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.session.Configuration;
/**
* ForEach标签
* @author Clinton Begin
*/
public class ForEachSqlNode implements SqlNode {
/**
* foreach的
*/
public static final String ITEM_PREFIX = "__frch_";
private final ExpressionEvaluator evaluator;
/**
* foreach标签的collection属性,循环的集合对象名
*/
private final String collectionExpression;
/**
* forEach标签下的子节点,封装成MixedSqlNode
*/
private final SqlNode contents;
/**
* foreach标签的open属性,foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选。
*/
private final String open;
/**
* foreach标签的close属性,foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
*/
private final String close;
/**
* foreach标签的separator属性,元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选
*/
private final String separator;
/**
* foreach标签的item属性,集合对象的元素名
*/
private final String item;
/**
* foreach标签的index属性,在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
*/
private final String index;
/**
* mybatis全局配置信息
*/
private final Configuration configuration;
/**
*
* @param configuration mybatis全局配置信息
* @param contents forEach标签下的子节点,封装成MixedSqlNode
* @param collectionExpression foreach标签的collection属性,循环的集合对象名
* @param index foreach标签的index属性,在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
* @param item foreach标签的item属性,集合对象的元素名
* @param open foreach标签的open属性,foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选。
* @param close foreach标签的close属性,foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
* @param separator foreach标签的separator属性,元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选
*/
public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, String index, String item, String open, String close, String separator) {
this.evaluator = new ExpressionEvaluator();
this.collectionExpression = collectionExpression;
this.contents = contents;
this.open = open;
this.close = close;
this.separator = separator;
this.index = index;
this.item = item;
this.configuration = configuration;
}
@Override
public boolean apply(DynamicContext context) {
//获取参数对象
Map bindings = context.getBindings();
//获取expression对应在parameterObject的对象的Iterable对象
final Iterable> iterable = evaluator.evaluateIterable(collectionExpression, bindings);
//没有元素就直接返回true
if (!iterable.iterator().hasNext()) {
return true;
}
boolean first = true;
//将open添加到context中
applyOpen(context);
// 迭代索引
int i = 0;
for (Object o : iterable) {
DynamicContext oldContext = context;
//对content进行装饰成prefixedContext
if (first || separator == null) {
context = new PrefixedContext(context, "");//首个元素不需要前缀,所以prefix为空字符串
} else {
context = new PrefixedContext(context, separator);
}
int uniqueNumber = context.getUniqueNumber();
// Issue #709
if (o instanceof Map.Entry) {
// entry 集合项索引为 key,集合项为 value
@SuppressWarnings("unchecked")
Map.Entry mapEntry = (Map.Entry) o;
applyIndex(context, mapEntry.getKey(), uniqueNumber);
applyItem(context, mapEntry.getValue(), uniqueNumber);
} else {
// 绑定集合项索引关系
applyIndex(context, i, uniqueNumber);
// 绑定集合项关系
applyItem(context, o, uniqueNumber);
}
// 对解析的表达式进行替换,如 idx = #{index} AND itm = #{item} 替换为 idx = #{__frch_index_1} AND itm = #{__frch_item_1}
contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
if (first) {
//只要没有应用前缀,first就一直为true,正常情况下,这里到这一步,isPrefixApplied一般为false。
first = !((PrefixedContext) context).isPrefixApplied();
}
context = oldContext;
i++;
}
//将close添加到context中
applyClose(context);
//移除item绑定关系
context.getBindings().remove(item);
//移除index绑定关系
context.getBindings().remove(index);
return true;
}
/**
* 绑定集合项索引关系
* @param context PrefixedContext实例
* @param o index值
* @param i {@code context} 的唯一数字 context.getUniqueNumber()
*/
private void applyIndex(DynamicContext context, Object o, int i) {
if (index != null) {
context.bind(index, o);
context.bind(itemizeItem(index, i), o);
}
}
/**
* 绑定集合项关系
* @param context PrefixedContext实例
* @param o index值
* @param i {@code context} 的唯一数字 context.getUniqueNumber()
*/
private void applyItem(DynamicContext context, Object o, int i) {
if (item != null) {
context.bind(item, o);
context.bind(itemizeItem(item, i), o);
}
}
/**
* 应用 {@link #open}
* @param context 用于记录动态SQL语句解析结果的容器
*/
private void applyOpen(DynamicContext context) {
if (open != null) {
context.appendSql(open);
}
}
/**
* 应用 {@link #close}
* @param context 用于记录动态SQL语句解析结果的容器
*/
private void applyClose(DynamicContext context) {
if (close != null) {
context.appendSql(close);
}
}
/**
* 组装列表项名
* @param item 项名
* @param i 位置
* @return {@link #ITEM_PREFIX} + item + "_" + i
*/
private static String itemizeItem(String item, int i) {
return ITEM_PREFIX + item + "_" + i;
}
/**
* 对解析的表达式进行替换的DynamincContext装饰类
*/
private static class FilteredDynamicContext extends DynamicContext {
/**
* 委托类对象
*/
private final DynamicContext delegate;
/**
* {@link #delegate} 的 uniqueNumber
*/
private final int index;
/**
* foreach标签的index属性,在list和数组中,index是元素的序号,
* 在map中,index是元素的key,该参数可选。
*/
private final String itemIndex;
/**
* foreach标签的item属性,集合对象的元素名
*/
private final String item;
/**
*
* @param configuration mybatis全局配置信息
* @param delegate 委托类对象
* @param itemIndex foreach标签的index属性,在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
* @param item foreach标签的item属性,集合对象的元素名
* @param i {@code delegate} 的 uniqueNumber
*/
public FilteredDynamicContext(Configuration configuration,DynamicContext delegate, String itemIndex, String item, int i) {
super(configuration, null);
this.delegate = delegate;
this.index = i;
this.itemIndex = itemIndex;
this.item = item;
}
@Override
public Map getBindings() {
return delegate.getBindings();
}
@Override
public void bind(String name, Object value) {
delegate.bind(name, value);
}
@Override
public String getSql() {
return delegate.getSql();
}
@Override
public void appendSql(String sql) {
GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> {
// 对解析的表达式进行替换,如 idx = #{index} AND itm = #{item} 替换为 idx = #{__frch_index_1} AND itm = #{__frch_item_1}
String newContent = content.replaceFirst("^\\s*" + item + "(?![^.,:\\s])", itemizeItem(item, index));
if (itemIndex != null && newContent.equals(content)) {
newContent = content.replaceFirst("^\\s*" + itemIndex + "(?![^.,:\\s])", itemizeItem(itemIndex, index));
}
return "#{" + newContent + "}";
});
delegate.appendSql(parser.parse(sql));
}
@Override
public int getUniqueNumber() {
return delegate.getUniqueNumber();
}
}
/**
* 带prefix的记录动态SQL语句解析结果的容器
*/
private class PrefixedContext extends DynamicContext {
/**
* 委托对象
*/
private final DynamicContext delegate;
/**
* 前缀
*/
private final String prefix;
/**
* 已经应用了前缀的标记
*/
private boolean prefixApplied;
/**
*
* @param delegate 委托对象
* @param prefix 前缀
*/
public PrefixedContext(DynamicContext delegate, String prefix) {
super(configuration, null);
this.delegate = delegate;
this.prefix = prefix;
this.prefixApplied = false;
}
/**
* 是否已经应用了前缀的标记
* @return
*/
public boolean isPrefixApplied() {
return prefixApplied;
}
@Override
public Map getBindings() {
return delegate.getBindings();
}
@Override
public void bind(String name, Object value) {
delegate.bind(name, value);
}
@Override
public void appendSql(String sql) {
//如果还没有添加prefix,且sql经过修剪后长度不为0,就会先添加prefix到delegate
if (!prefixApplied && sql != null && sql.trim().length() > 0) {
delegate.appendSql(prefix);
prefixApplied = true;
}
//添加sql到delegate
delegate.appendSql(sql);
}
@Override
public String getSql() {
return delegate.getSql();
}
@Override
public int getUniqueNumber() {
return delegate.getUniqueNumber();
}
}
}
IfSqlNode
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
/**
* if标签,动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分
* @author Clinton Begin
*/
public class IfSqlNode implements SqlNode {
/**
* OGNL 表达式计算工具
*/
private final ExpressionEvaluator evaluator;
/**
* ognl表达式
*/
private final String test;
/**
* if标签下的字节点,封装成MixedSqlNode
*/
private final SqlNode contents;
/**
*
* @param contents if标签下的字节点,封装成MixedSqlNode
* @param test ognl表达式
*/
public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
}
@Override
public boolean apply(DynamicContext context) {
// 根据 test 表达式判断当前节点是否生效
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
MixedSqlNode
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.List;
/**
* 混合的SQL节点,里面存储着sqlNode结合,当调用apply方法时,会遍历其sqlNode集合的元素,调用元素的apply进行处理。
* @author Clinton Begin
*/
public class MixedSqlNode implements SqlNode {
private final List contents;
public MixedSqlNode(List contents) {
this.contents = contents;
}
@Override
public boolean apply(DynamicContext context) {
contents.forEach(node -> node.apply(context));
return true;
}
}
TrimSqlNode
/**
* Copyright 2009-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.ibatis.session.Configuration;
/**
* Trim标签
* @author Clinton Begin
*/
public class TrimSqlNode implements SqlNode {
/**
* trim标签下的字节点,封装成MixedSqlNode
*/
private final SqlNode contents;
/**
* trim标签的prefix属性,给sql语句拼接的前缀
*/
private final String prefix;
/**
* trim标签的suffix属性,给sql语句拼接的后缀
*/
private final String suffix;
/**
* trim标签的prefixOverrides属性
*
/**
* Copyright 2009-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.session.Configuration;
/**
* set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,
* 因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。
*
* 实现逻辑是套用TrimSqlNode
*
* @author Clinton Begin
*/
public class SetSqlNode extends TrimSqlNode {
private static final List COMMA = Collections.singletonList(",");
/**
*
* @param configuration mybatis全局配置信息
* @param contents set标签下的字节点,封装成MixedSqlNode
*/
public SetSqlNode(Configuration configuration,SqlNode contents) {
/**
* 设置为'SET'为前缀,设置COMMA为trimSqlNode的prefixOverride,到时候调用apply
* 方法就会自动添加'SET'作为前缀,并覆盖掉'SET'后面COMMA的元素
*/
super(configuration, contents, "SET", COMMA, null, COMMA);
}
}
StaticTextSqlNode
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
/**
* 静态文本SQL节点,其apply方法使得只是将存储的文本内容直接添加到DynamicContext对象中
* @author Clinton Begin
*/
public class StaticTextSqlNode implements SqlNode {
/**
* 静态文本
*/
private final String text;
/**
*
* @param text 静态文本
*/
public StaticTextSqlNode(String text) {
this.text = text;
}
/**
* 将{@link #text} 直接添加到 {@code context}
* @param context 一个用于记录动态SQL语句解析结果的容器
* @return 写死返回true
*/
@Override
public boolean apply(DynamicContext context) {
context.appendSql(text);
return true;
}
}
TextSqlNode
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.regex.Pattern;
import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.parsing.TokenHandler;
import org.apache.ibatis.scripting.ScriptingException;
import org.apache.ibatis.type.SimpleTypeRegistry;
/**
* 文本SQL节点。用传入的实际参数对象中的属性值对${}进行直接替换,并且不会进行任何检查!
* @author Clinton Begin
*/
public class TextSqlNode implements SqlNode {
/**
* 文本内容
*/
private final String text;
/**
* 需要匹配的正则表达式
*/
private final Pattern injectionFilter;
/**
*
* @param text 文本内容
*/
public TextSqlNode(String text) {
this(text, null);
}
/**
*
* @param text 文本内容
* @param injectionFilter 需要匹配的正则表达式
*/
public TextSqlNode(String text, Pattern injectionFilter) {
this.text = text;
this.injectionFilter = injectionFilter;
}
/**
* 是否动态SQL。当sql中包含有${}时,就认为是动态SQL
*/
public boolean isDynamic() {
//DynamicCheckerTokenParse:当sql中包含有${}时,就认为是动态SQL
DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
GenericTokenParser parser = createParser(checker);
//GenericTokenParser会将检查到的'${XXX}'的XXX传递给DynamicCheckerTokenParser的handleToken方法进行处理.
parser.parse(text);
return checker.isDynamic();
}
/**
* 用传入的实际参数对象中的属性值对${}进行直接替换,并且不会进行任何检查!
* @param context 用于记录动态SQL语句解析结果的容器
* @return 写死了true
*/
@Override
public boolean apply(DynamicContext context) {
GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
/**
* GenericTokenParser会将检查到的'${XXX}'的XXX传递给BindingTokenParser的handleToken方法进行处理.
* 将解析处理的结果添加到context中。
*/
context.appendSql(parser.parse(text));
return true;
}
/**
* 创建通用token解析器
* @param handler token处理器
* @return 专门处理'${...}'形式的token的通用token解析器
*/
private GenericTokenParser createParser(TokenHandler handler) {
return new GenericTokenParser("${", "}", handler);
}
/**
* 用于获取替换${}的真实参数值
*/
private static class BindingTokenParser implements TokenHandler {
private DynamicContext context;
/**
* 正则表达式
*/
private Pattern injectionFilter;
public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
this.context = context;
this.injectionFilter = injectionFilter;
}
@Override
public String handleToken(String content) {
//TODO 没有看懂这里干嘛的
Object parameter = context.getBindings().get("_parameter");
if (parameter == null) {
context.getBindings().put("value", null);
} else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
context.getBindings().put("value", parameter);
}
// 从传入的参数中获取到${}对应的值并对${}进行替换
Object value = OgnlCache.getValue(content, context.getBindings());
String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null"
//检查值是否匹配正则表达式,不匹配抛出异常
checkInjection(srtValue);
return srtValue;
}
/**
* 检查值是否匹配正则表达式,不匹配抛出异常
* @param value 值
*/
private void checkInjection(String value) {
if (injectionFilter != null && !injectionFilter.matcher(value).matches()) {
throw new ScriptingException("Invalid input. Please conform to regex" + injectionFilter.pattern());
}
}
}
/**
* 用于sql的动态检测,当sql中包含有${}时就认为是动态SQL
*/
private static class DynamicCheckerTokenParser implements TokenHandler {
/**
* 是否动态SQL,当sql中包含有${}时就认为是动态SQL
*/
private boolean isDynamic;
public DynamicCheckerTokenParser() {
// Prevent Synthetic Access
}
/**
*
* @return {@see #isDynamic}
*/
public boolean isDynamic() {
return isDynamic;
}
/**
* 当GenericTokenParser检测到sql中包含有${}时,只是简单的标记为动态
*/
@Override
public String handleToken(String content) {
this.isDynamic = true;
return null;
}
}
}
VarDecSqlNode
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
/**
* OGNL SqlNode
* @author Frank D. Martinez [mnesarco]
*/
public class VarDeclSqlNode implements SqlNode {
/**
* 变量名
*/
private final String name;
/**
* 表达式
*/
private final String expression;
/**
*
* @param var 变量名
* @param exp 表达式
*/
public VarDeclSqlNode(String var, String exp) {
name = var;
expression = exp;
}
@Override
public boolean apply(DynamicContext context) {
final Object value = OgnlCache.getValue(expression, context.getBindings());
context.bind(name, value);
return true;
}
}
WhereSqlNode
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
import java.util.Arrays;
import java.util.List;
import org.apache.ibatis.session.Configuration;
/**
* where 标签知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。
* 而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。
*
* 实现逻辑是套用TrimSqlNode
*
* @author Clinton Begin
*/
public class WhereSqlNode extends TrimSqlNode {
private static List prefixList = Arrays.asList("AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t");
/**
*
* @param configuration mybatis全局配置信息
* @param contents where标签下的字节点,封装成MixedSqlNode
*/
public WhereSqlNode(Configuration configuration, SqlNode contents) {
/**
* 设置为'WHERE'为前缀,设置prefixList为trimSqlNode的prefixOverride,到时候调用apply
* 方法就会自动添加'WHERE'作为前缀,并覆盖掉'WHERE'后面prefixList的元素
*/
super(configuration, contents, "WHERE", prefixList, null, null);
}
}
原文地址:http://www.cnblogs.com/Kavlez/p/4268601.html Enumeration
于Java 1.5增加的enum type...enum type是由一组固定的常量组成的类型,比如四个季节、扑克花色。在出现enum type之前,通常用一组int常量表示枚举类型。比如这样:
public static final int APPLE_FUJI = 0
第二章 Getting Started
1.Hive最大的局限性是什么?一是不支持行级别的增删改(insert, delete, update)二是查询性能非常差(基于Hadoop MapReduce),不适合延迟小的交互式任务三是不支持事务2. Hive MetaStore是干什么的?Hive persists table schemas and other system metadata.
/*
* 0.use a TwoWayLinkedList to store the path.when the node can't be path,you should/can delete it.
* 1.curSum==exceptedSum:if the lastNode is TreeNode,printPath();delete the node otherwise
//js获取项目根路径,如: http://localhost:8083/uimcardprj
function getRootPath(){
//获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp
var curWwwPath=window.document.locati
在Linux下面部 署应用的时候,有时候会遇上Socket/File: Can’t open so many files的问题;这个值也会影响服务器的最大并发数,其实Linux是有文件句柄限制的,而且Linux默认不是很高,一般都是1024,生产服务器用 其实很容易就达到这个数量。下面说的是,如何通过正解配置来改正这个系统默认值。因为这个问题是我配置Nginx+php5时遇到了,所以我将这篇归纳进