Skip to content

Commit bb893a5

Browse files
authored
Memory usage optimization when parsing mapper files
* Less object creation in GenericTokenParser. * Optimized <include /> resolution logic.
2 parents ecc148e + 6792d47 commit bb893a5

File tree

2 files changed

+73
-73
lines changed

2 files changed

+73
-73
lines changed

src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
*/
1616
package org.apache.ibatis.builder.xml;
1717

18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.Properties;
21+
1822
import org.apache.ibatis.builder.BuilderException;
1923
import org.apache.ibatis.builder.IncompleteElementException;
2024
import org.apache.ibatis.builder.MapperBuilderAssistant;
@@ -24,8 +28,6 @@
2428
import org.w3c.dom.Node;
2529
import org.w3c.dom.NodeList;
2630

27-
import java.util.Properties;
28-
2931
/**
3032
* @author Frank D. Martinez [mnesarco]
3133
*/
@@ -45,34 +47,19 @@ public void applyIncludes(Node source) {
4547
if (configurationVariables != null) {
4648
variablesContext.putAll(configurationVariables);
4749
}
48-
applyIncludes(source, variablesContext);
50+
applyIncludes(source, variablesContext, false);
4951
}
5052

5153
/**
5254
* Recursively apply includes through all SQL fragments.
5355
* @param source Include node in DOM tree
5456
* @param variablesContext Current context for static variables with values
5557
*/
56-
private void applyIncludes(Node source, final Properties variablesContext) {
58+
private void applyIncludes(Node source, final Properties variablesContext, boolean included) {
5759
if (source.getNodeName().equals("include")) {
58-
// new full context for included SQL - contains inherited context and new variables from current include node
59-
Properties fullContext;
60-
61-
String refid = getStringAttribute(source, "refid");
62-
// replace variables in include refid value
63-
refid = PropertyParser.parse(refid, variablesContext);
64-
Node toInclude = findSqlFragment(refid);
65-
Properties newVariablesContext = getVariablesContext(source, variablesContext);
66-
if (!newVariablesContext.isEmpty()) {
67-
// merge contexts
68-
fullContext = new Properties();
69-
fullContext.putAll(variablesContext);
70-
fullContext.putAll(newVariablesContext);
71-
} else {
72-
// no new context - use inherited fully
73-
fullContext = variablesContext;
74-
}
75-
applyIncludes(toInclude, fullContext);
60+
Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext);
61+
Properties toIncludeContext = getVariablesContext(source, variablesContext);
62+
applyIncludes(toInclude, toIncludeContext, true);
7663
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
7764
toInclude = source.getOwnerDocument().importNode(toInclude, true);
7865
}
@@ -83,19 +70,18 @@ private void applyIncludes(Node source, final Properties variablesContext) {
8370
toInclude.getParentNode().removeChild(toInclude);
8471
} else if (source.getNodeType() == Node.ELEMENT_NODE) {
8572
NodeList children = source.getChildNodes();
86-
for (int i=0; i<children.getLength(); i++) {
87-
applyIncludes(children.item(i), variablesContext);
73+
for (int i = 0; i < children.getLength(); i++) {
74+
applyIncludes(children.item(i), variablesContext, included);
8875
}
89-
} else if (source.getNodeType() == Node.ATTRIBUTE_NODE && !variablesContext.isEmpty()) {
90-
// replace variables in all attribute values
91-
source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
92-
} else if (source.getNodeType() == Node.TEXT_NODE && !variablesContext.isEmpty()) {
76+
} else if (included && source.getNodeType() == Node.TEXT_NODE
77+
&& !variablesContext.isEmpty()) {
9378
// replace variables ins all text nodes
9479
source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
9580
}
9681
}
9782

98-
private Node findSqlFragment(String refid) {
83+
private Node findSqlFragment(String refid, Properties variables) {
84+
refid = PropertyParser.parse(refid, variables);
9985
refid = builderAssistant.applyCurrentNamespace(refid, true);
10086
try {
10187
XNode nodeToInclude = configuration.getSqlFragments().get(refid);
@@ -116,23 +102,29 @@ private String getStringAttribute(Node node, String name) {
116102
* @return variables context from include instance (no inherited values)
117103
*/
118104
private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
119-
Properties variablesContext = new Properties();
105+
Map<String, String> declaredProperties = null;
120106
NodeList children = node.getChildNodes();
121107
for (int i = 0; i < children.getLength(); i++) {
122108
Node n = children.item(i);
123109
if (n.getNodeType() == Node.ELEMENT_NODE) {
124110
String name = getStringAttribute(n, "name");
125-
String value = getStringAttribute(n, "value");
126111
// Replace variables inside
127-
value = PropertyParser.parse(value, inheritedVariablesContext);
128-
// Push new value
129-
Object originalValue = variablesContext.put(name, value);
130-
if (originalValue != null) {
112+
String value = PropertyParser.parse(getStringAttribute(n, "value"), inheritedVariablesContext);
113+
if (declaredProperties == null) {
114+
declaredProperties = new HashMap<String, String>();
115+
}
116+
if (declaredProperties.put(name, value) != null) {
131117
throw new BuilderException("Variable " + name + " defined twice in the same include definition");
132118
}
133119
}
134120
}
135-
return variablesContext;
121+
if (declaredProperties == null) {
122+
return inheritedVariablesContext;
123+
} else {
124+
Properties newProperties = new Properties();
125+
newProperties.putAll(inheritedVariablesContext);
126+
newProperties.putAll(declaredProperties);
127+
return newProperties;
128+
}
136129
}
137-
138130
}

src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,58 @@ public GenericTokenParser(String openToken, String closeToken, TokenHandler hand
3131
}
3232

3333
public String parse(String text) {
34+
if (text == null || text.isEmpty()) {
35+
return "";
36+
}
37+
char[] src = text.toCharArray();
38+
int offset = 0;
39+
// search open token
40+
int start = text.indexOf(openToken, offset);
41+
if (start == -1) {
42+
return text;
43+
}
3444
final StringBuilder builder = new StringBuilder();
35-
final StringBuilder expression = new StringBuilder();
36-
if (text != null && text.length() > 0) {
37-
char[] src = text.toCharArray();
38-
int offset = 0;
39-
// search open token
40-
int start = text.indexOf(openToken, offset);
41-
while (start > -1) {
42-
if (start > 0 && src[start - 1] == '\\') {
43-
// this open token is escaped. remove the backslash and continue.
44-
builder.append(src, offset, start - offset - 1).append(openToken);
45-
offset = start + openToken.length();
45+
StringBuilder expression = null;
46+
while (start > -1) {
47+
if (start > 0 && src[start - 1] == '\\') {
48+
// this open token is escaped. remove the backslash and continue.
49+
builder.append(src, offset, start - offset - 1).append(openToken);
50+
offset = start + openToken.length();
51+
} else {
52+
// found open token. let's search close token.
53+
if (expression == null) {
54+
expression = new StringBuilder();
4655
} else {
47-
// found open token. let's search close token.
4856
expression.setLength(0);
49-
builder.append(src, offset, start - offset);
50-
offset = start + openToken.length();
51-
int end = text.indexOf(closeToken, offset);
52-
while (end > -1) {
53-
if (end > offset && src[end - 1] == '\\') {
54-
// this close token is escaped. remove the backslash and continue.
55-
expression.append(src, offset, end - offset - 1).append(closeToken);
56-
offset = end + closeToken.length();
57-
end = text.indexOf(closeToken, offset);
58-
} else {
59-
expression.append(src, offset, end - offset);
60-
offset = end + closeToken.length();
61-
break;
62-
}
63-
}
64-
if (end == -1) {
65-
// close token was not found.
66-
builder.append(src, start, src.length - start);
67-
offset = src.length;
57+
}
58+
builder.append(src, offset, start - offset);
59+
offset = start + openToken.length();
60+
int end = text.indexOf(closeToken, offset);
61+
while (end > -1) {
62+
if (end > offset && src[end - 1] == '\\') {
63+
// this close token is escaped. remove the backslash and continue.
64+
expression.append(src, offset, end - offset - 1).append(closeToken);
65+
offset = end + closeToken.length();
66+
end = text.indexOf(closeToken, offset);
6867
} else {
69-
builder.append(handler.handleToken(expression.toString()));
68+
expression.append(src, offset, end - offset);
7069
offset = end + closeToken.length();
70+
break;
7171
}
7272
}
73-
start = text.indexOf(openToken, offset);
74-
}
75-
if (offset < src.length) {
76-
builder.append(src, offset, src.length - offset);
73+
if (end == -1) {
74+
// close token was not found.
75+
builder.append(src, start, src.length - start);
76+
offset = src.length;
77+
} else {
78+
builder.append(handler.handleToken(expression.toString()));
79+
offset = end + closeToken.length();
80+
}
7781
}
82+
start = text.indexOf(openToken, offset);
83+
}
84+
if (offset < src.length) {
85+
builder.append(src, offset, src.length - offset);
7886
}
7987
return builder.toString();
8088
}

0 commit comments

Comments
 (0)