在 XMLStatementBuilder 当中解析的时候,通过 LanguageDriver 创建了 SqlSource,这篇来分析 SqlSource 以及和它相关的一些类:

1
2
3
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}

SqlSource 接口只有一个方法,会返回 BoundSql 实例。

在 XMLScriptBuilder 当中创建 SqlSource 的方法:

1
2
3
4
5
6
7
8
9
10
11
public SqlSource parseScriptNode() {
List<SqlNode> contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}

上一篇已经分析了 SqlNode 相关的内容,也举了一个简单例子,那个例子当中因为会有 where 元素,所以 isDynamic 是 true,就会创建 DynamicSqlSource 的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DynamicSqlSource implements SqlSource {

private Configuration configuration;
private SqlNode rootSqlNode;

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

@Override
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();
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;
}
}

上面的内容后续分析,我们先来分析一个简单的 SqlNode 的执行过程。

IfSqlNode

IfSqlNode 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class IfSqlNode implements SqlNode {
private ExpressionEvaluator evaluator;
private String test;
private SqlNode contents;

public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
}

@Override
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}

}

IfSqlNode 当中只有一个 apply 方法,传递进来一个 DynamicContext 参数,因为每个 if 的节点都会根据参数进行一些判断是否为 true 动态生成部分 sql。

通过 ExpressionEvaluator 的 evaluateBoolean 来判断 test 的内容是否为 true。

如果是 true,那么执行 contents 的 apply 方法。

ExpressionEvaluator 里面主要是对 OGNL 表达式的封装。

因为最后的 SqlNode 都是文本类型的,所以看下 StaticTextSqlNode 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StaticTextSqlNode implements SqlNode {
private String text;

public StaticTextSqlNode(String text) {
this.text = text;
}

@Override
public boolean apply(DynamicContext context) {
context.appendSql(text);
return true;
}

}

很简单,把 StaticTextSqlNode 的 text 内容添加到 context 当中去。

DynamicContext 分析

继续分析上文当中的 DynamicSqlSource 的 getBoundSql 方法,在 getBoundSql 方法当中会创建 DynamicContext 的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
DynamicContext context = new DynamicContext(configuration, parameterObject);

public class DynamicContext {

public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";

static {
OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}

private final ContextMap bindings;
private final StringBuilder sqlBuilder = new StringBuilder();
private int uniqueNumber = 0;

public DynamicContext(Configuration configuration, Object parameterObject) {
if (parameterObject != null && !(parameterObject instanceof 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 Map<String, 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++;
}

static class ContextMap extends HashMap<String, Object> {
private static final long serialVersionUID = 2977601501966151582L;

private MetaObject parameterMetaObject;
public ContextMap(MetaObject parameterMetaObject) {
this.parameterMetaObject = parameterMetaObject;
}

@Override
public Object get(Object key) {
String strKey = (String) key;
if (super.containsKey(strKey)) {
return super.get(strKey);
}

if (parameterMetaObject != null) {
// issue #61 do not modify the context when reading
return parameterMetaObject.getValue(strKey);
}

return null;
}
}

static class ContextAccessor implements PropertyAccessor {

@Override
public Object getProperty(Map context, Object target, Object name)
throws OgnlException {
Map map = (Map) target;

Object result = map.get(name);
if (map.containsKey(name) || result != null) {
return result;
}

Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
if (parameterObject instanceof Map) {
return ((Map)parameterObject).get(name);
}

return null;
}

@Override
public void setProperty(Map context, Object target, Object name, Object value)
throws OgnlException {
Map<Object, Object> map = (Map<Object, Object>) target;
map.put(name, value);
}

@Override
public String getSourceAccessor(OgnlContext arg0, Object arg1, Object arg2) {
return null;
}

@Override
public String getSourceSetter(OgnlContext arg0, Object arg1, Object arg2) {
return null;
}
}
}

首先看 DynamicContext 的几个实例属性:

1
2
3
private final ContextMap bindings;//OGNL 使用的上下文
private final StringBuilder sqlBuilder = new StringBuilder();//记录 Sql 的 StringBuilder
private int uniqueNumber = 0;

再来看 DynamicContext 的构造方法:

1
2
3
4
5
6
7
8
9
10
public DynamicContext(Configuration configuration, Object parameterObject) {
if (parameterObject != null && !(parameterObject instanceof 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());
}

构造方法的主要工作就是创建 ContextMap 的实例 bindings,这个 bindings 是用于在 OGNL 表达式当中解析 sql 时候使用。

内部其他的内容就是静态内部类 ContextMap 和 ContextAccessor,主要也是便于 OGNL 的使用。

继续分析 DynamicSqlSource

刚才相当于倒序的分析了 DynamicSqlSource 的 getBoundSql 的执行过程,先分析了 IfSqlNode,然后分析了 DynamicContext,下面继续进行:

1
2
3
4
5
6
7
8
9
10
11
12
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();
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 实例,然后通过 parse 方法解析新的 SqlSource。SqlSourceBuilder 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
//...省略部分代码
}

ParameterMappingTokenHandler 和 GenericTokenParser 的体系以后文章分析,这个里面就是把在 XML 写的 #{id} 这样的形式替换成 ?。并且创建了 StaticSqlSource实例返回。

StaticSqlSource 代码很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StaticSqlSource implements SqlSource {

private String sql;
private List<ParameterMapping> parameterMappings;
private Configuration configuration;

public StaticSqlSource(Configuration configuration, String sql) {
this(configuration, sql, null);
}

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

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

}

再次回归到 DynamicSqlSource 的 getBoundSql 当中,下一步是:

1
2
3
4
5
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;

看一下 BoundSql 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BoundSql {

private String sql;
private List<ParameterMapping> parameterMappings;
private Object parameterObject;
private Map<String, Object> additionalParameters;
private MetaObject metaParameters;

public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap<String, Object>();
this.metaParameters = configuration.newMetaObject(additionalParameters);
}
//省略get set 方法
}

下篇继续分析细节。

—EOF—