Skip to content

Commit 5f9a168

Browse files
committed
Closes #98. Detect and pre-process static statements during startup so
the execution becames much faster (x2). No need to use "raw" anymore because now "raw" is automatically applied.
1 parent 50e91d0 commit 5f9a168

File tree

8 files changed

+128
-85
lines changed

8 files changed

+128
-85
lines changed

src/main/java/org/apache/ibatis/scripting/defaults/RawLanguageDriver.java

+1-12
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
import org.apache.ibatis.parsing.XNode;
2323
import org.apache.ibatis.scripting.LanguageDriver;
2424
import org.apache.ibatis.session.Configuration;
25-
import org.w3c.dom.Node;
26-
import org.w3c.dom.NodeList;
2725

2826
public class RawLanguageDriver implements LanguageDriver {
2927

@@ -32,16 +30,7 @@ public ParameterHandler createParameterHandler(MappedStatement mappedStatement,
3230
}
3331

3432
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
35-
StringBuilder contents = new StringBuilder();
36-
NodeList children = script.getNode().getChildNodes();
37-
for (int i = 0; i < children.getLength(); i++) {
38-
XNode child = script.newXNode(children.item(i));
39-
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
40-
String data = child.getStringBody("");
41-
contents.append(data);
42-
}
43-
}
44-
return new RawSqlSource(configuration, contents.toString(), parameterType);
33+
return new RawSqlSource(configuration, script, parameterType);
4534
}
4635

4736
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java

+20
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,37 @@
2020
import org.apache.ibatis.builder.SqlSourceBuilder;
2121
import org.apache.ibatis.mapping.BoundSql;
2222
import org.apache.ibatis.mapping.SqlSource;
23+
import org.apache.ibatis.parsing.XNode;
2324
import org.apache.ibatis.session.Configuration;
25+
import org.w3c.dom.Node;
26+
import org.w3c.dom.NodeList;
2427

2528
public class RawSqlSource implements SqlSource {
2629

2730
private final SqlSource sqlSource;
2831

32+
public RawSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
33+
this(configuration, getString(script), parameterType);
34+
}
35+
2936
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
3037
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
3138
Class<?> clazz = parameterType == null ? Object.class : parameterType;
3239
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
3340
}
41+
42+
private static String getString(XNode script) {
43+
StringBuilder contents = new StringBuilder();
44+
NodeList children = script.getNode().getChildNodes();
45+
for (int i = 0; i < children.getLength(); i++) {
46+
XNode child = script.newXNode(children.item(i));
47+
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
48+
String data = child.getStringBody("");
49+
contents.append(data);
50+
}
51+
}
52+
return contents.toString();
53+
}
3454

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

src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2012 the original author or authors.
2+
* Copyright 2009-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,12 +25,23 @@ public class TextSqlNode implements SqlNode {
2525
public TextSqlNode(String text) {
2626
this.text = text;
2727
}
28+
29+
public boolean isDynamic() {
30+
DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
31+
GenericTokenParser parser = createParser(checker);
32+
parser.parse(text);
33+
return checker.isDynamic();
34+
}
2835

2936
public boolean apply(DynamicContext context) {
30-
GenericTokenParser parser = new GenericTokenParser("${", "}", new BindingTokenParser(context));
37+
GenericTokenParser parser = createParser(new BindingTokenParser(context));
3138
context.appendSql(parser.parse(text));
3239
return true;
3340
}
41+
42+
private GenericTokenParser createParser(TokenHandler handler) {
43+
return new GenericTokenParser("${", "}", handler);
44+
}
3445

3546
private static class BindingTokenParser implements TokenHandler {
3647

@@ -52,4 +63,18 @@ public String handleToken(String content) {
5263
}
5364
}
5465

66+
private static class DynamicCheckerTokenParser implements TokenHandler {
67+
68+
private boolean isDynamic;
69+
70+
public boolean isDynamic() {
71+
return isDynamic;
72+
}
73+
74+
public String handleToken(String content) {
75+
this.isDynamic = true;
76+
return null;
77+
}
78+
}
79+
5580
}

src/main/java/org/apache/ibatis/scripting/xmltags/XMLLanguageDriver.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515
*/
1616
package org.apache.ibatis.scripting.xmltags;
1717

18-
import java.util.ArrayList;
19-
import java.util.List;
20-
2118
import org.apache.ibatis.executor.parameter.ParameterHandler;
2219
import org.apache.ibatis.mapping.BoundSql;
2320
import org.apache.ibatis.mapping.MappedStatement;
2421
import org.apache.ibatis.mapping.SqlSource;
2522
import org.apache.ibatis.parsing.XNode;
2623
import org.apache.ibatis.scripting.LanguageDriver;
2724
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
25+
import org.apache.ibatis.scripting.defaults.RawSqlSource;
2826
import org.apache.ibatis.session.Configuration;
2927

3028
public class XMLLanguageDriver implements LanguageDriver {
@@ -34,19 +32,21 @@ public ParameterHandler createParameterHandler(MappedStatement mappedStatement,
3432
}
3533

3634
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
37-
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script);
35+
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
3836
return builder.parseScriptNode();
3937
}
4038

4139
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
4240
if (script.startsWith("<script>")) { // issue #3
43-
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script);
41+
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
4442
return builder.parseScriptNode();
4543
} else {
46-
List<SqlNode> contents = new ArrayList<SqlNode>();
47-
contents.add(new TextSqlNode(script.toString()));
48-
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
49-
return new DynamicSqlSource(configuration, rootSqlNode);
44+
TextSqlNode textSqlNode = new TextSqlNode(script);
45+
if (textSqlNode.isDynamic()) {
46+
return new DynamicSqlSource(configuration, textSqlNode);
47+
} else {
48+
return new RawSqlSource(configuration, script, parameterType);
49+
}
5050
}
5151
}
5252

src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java

+29-4
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,47 @@
2626
import org.apache.ibatis.mapping.SqlSource;
2727
import org.apache.ibatis.parsing.XNode;
2828
import org.apache.ibatis.parsing.XPathParser;
29+
import org.apache.ibatis.scripting.defaults.RawSqlSource;
2930
import org.apache.ibatis.session.Configuration;
3031
import org.w3c.dom.Node;
3132
import org.w3c.dom.NodeList;
3233

3334
public class XMLScriptBuilder extends BaseBuilder {
3435

3536
private XNode context;
37+
private boolean isDynamic;
38+
private Class<?> parameterType;
3639

3740
public XMLScriptBuilder(Configuration configuration, XNode context) {
41+
this(configuration, context, null);
42+
}
43+
44+
public XMLScriptBuilder(Configuration configuration, String context) {
45+
this(configuration, context, null);
46+
}
47+
48+
public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
3849
super(configuration);
3950
this.context = context;
51+
this.parameterType = parameterType;
4052
}
4153

42-
public XMLScriptBuilder(Configuration configuration, String context) {
54+
public XMLScriptBuilder(Configuration configuration, String context, Class<?> parameterType) {
4355
super(configuration);
4456
XPathParser parser = new XPathParser(context, false, configuration.getVariables(), new XMLMapperEntityResolver());
4557
this.context = parser.evalNode("/script");
58+
this.parameterType = parameterType;
4659
}
47-
60+
4861
public SqlSource parseScriptNode() {
4962
List<SqlNode> contents = parseDynamicTags(context);
5063
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
51-
SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
64+
SqlSource sqlSource = null;
65+
if (isDynamic) {
66+
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
67+
} else {
68+
sqlSource = new RawSqlSource(configuration, context, parameterType);
69+
}
5270
return sqlSource;
5371
}
5472

@@ -61,13 +79,20 @@ private List<SqlNode> parseDynamicTags(XNode node) {
6179
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE
6280
|| child.getNode().getNodeType() == Node.TEXT_NODE) {
6381
String data = child.getStringBody("");
64-
contents.add(new TextSqlNode(data));
82+
TextSqlNode textSqlNode = new TextSqlNode(data);
83+
if (textSqlNode.isDynamic()) {
84+
contents.add(textSqlNode);
85+
isDynamic = true;
86+
} else {
87+
contents.add(new StaticTextSqlNode(data));
88+
}
6589
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE && !"selectKey".equals(nodeName)) { // issue #628
6690
NodeHandler handler = nodeHandlers.get(nodeName);
6791
if (handler == null) {
6892
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
6993
}
7094
handler.handleNode(child, contents);
95+
isDynamic = true;
7196
}
7297
}
7398
return contents;

src/site/es/xdoc/dynamic-sql.xml

+19-29
Original file line numberDiff line numberDiff line change
@@ -187,47 +187,37 @@ AND title like ‘someTitle’]]></source>
187187
<p>Desde la versión 3.2 MyBatis soporta la adición de lenguajes de scripting de forma que puedes
188188
añadir un driver de lenguaje y usar dicho lenguaje para escribir tus sentencias SQL.
189189
</p>
190-
<p>Hay dos lenguajes predefinidos:</p>
191-
<table>
192-
<thead>
193-
<tr><td>Alias</td><td>Driver</td>
194-
</tr>
195-
</thead>
196-
<tbody>
197-
<tr><td>xml</td><td>XmlLanguageDriver</td></tr>
198-
<tr><td>raw</td><td>RawLanguageDriver</td></tr>
199-
</tbody>
200-
</table>
201-
<p>El lenguaje <code>xml</code> es el lenguaje por defecto. Permite ejecutar todos los tags dinámicos que hemos visto en las secciones anteriores.</p>
202-
<p>El lenguaje <code>raw</code> es en realidad la ausencia de lenguaje. Con esta configuración MyBatis sólo realiza la sustitución de parámetros
203-
y pasa la sentencia al driver de base de datos. Como supondrás el lenguaje <code>raw</code> es más rápido que el <code>xml</code>.
204-
</p>
205-
<p>Puedes indicar le lenguaje por defecto para tu proyecto configurandolo en el fichero mybatis-config.xml:</p>
206-
<source><![CDATA[<settings>
207-
<setting name="defaultScriptingLanguage" value="raw"/>
190+
<p>Puedes añadir un lenguaje implementando el siguiente interfaz:</p>
191+
<source><![CDATA[public interface LanguageDriver {
192+
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
193+
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
194+
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
195+
}]]></source>
196+
<p>Una vez tienes tu driver de lenguaje puedes puede hacer que sea el de uso por defecto estableciéndolo en el fichero mybatis-config.xml:</p>
197+
<source><![CDATA[<typeAliases>
198+
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
199+
</typeAliases>
200+
<settings>
201+
<setting name="defaultScriptingLanguage" value="myLanguage"/>
208202
</settings>
209-
]]></source>
210-
<p>Puedes indicar el lenguaje que quieres usar en un statement específico añadiendo el atributo <code>lang</code> de la siguiente forma:
203+
]]></source>
204+
<p>En lugar de cambiar el valor por defecto, puedes indicar el lenguaje para un statement específico
205+
añadiendo el atributo <code>lang</code> de la siguiente forma:
211206
</p>
212-
<source><![CDATA[<select id="selectBlog" lang="raw">
207+
<source><![CDATA[<select id="selectBlog" lang="myLanguage">
213208
SELECT * FROM BLOG
214209
</select>]]></source>
215210
<p>O, en el caso de que uses mappers, usando la anotación <code>@Lang</code>:</p>
216211
<source><![CDATA[public interface Mapper {
217-
@Lang(RawLanguageDriver.class)
212+
@Lang(MyLanguageDriver.class)
218213
@Select("SELECT * FROM BLOG")
219214
List<Blog> selectBlog();
220215
}]]></source>
221216

222217
<p><span class="label important">NOTA</span> Puedes utilizar Apache Velocity como lenguaje dinámico. Echa un vistazo al proyecto MyBatis-Velocity para conocer los detalles.</p>
223218

224-
<p>También puedes implementar tu propio lenguaje implementado el siguiente interfaz:</p>
225-
<source><![CDATA[public interface LanguageDriver {
226-
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
227-
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
228-
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
229-
}]]></source>
230-
<p></p>
219+
<p>Todos los tags que has visto en las secciones previas se proporcionan por el lenguaje por defecto de MyBatis cuyo driver es
220+
<code>org.apache.ibatis.scripting.xmltags.XmlLanguageDriver</code> que tiene un alias llamado <code>xml</code>.</p>
231221
</subsection>
232222
</section>
233223
</body>

src/site/xdoc/dynamic-sql.xml

+17-28
Original file line numberDiff line numberDiff line change
@@ -186,47 +186,36 @@ AND title like ‘someTitle’]]></source>
186186
<p>Starting from version 3.2 MyBatis supports pluggable scripting languages,
187187
so you can plug a language driver and use that language to write your dynamic
188188
SQL queries.</p>
189-
<p>There are two built-in languages:</p>
190-
<table>
191-
<thead>
192-
<tr><td>Alias</td><td>Driver</td>
193-
</tr>
194-
</thead>
195-
<tbody>
196-
<tr><td>xml</td><td>XmlLanguageDriver</td></tr>
197-
<tr><td>raw</td><td>RawLanguageDriver</td></tr>
198-
</tbody>
199-
</table>
200-
<p>The <code>xml</code> language is the default one. It is able to execute all the dynamic tags we saw in the previous sections.</p>
201-
<p>The <code>raw</code> language is in fact the absence of language. When using this setting MyBatis just performs the
202-
parameter substitution and passes the statement to the database driver. As you may guess, the <code>raw</code> language
203-
is much faster than the <code>xml</code> language.
204-
</p>
205-
<p>You can change the default language for your project by configuring it in the mybatis-config.xml file:</p>
206-
<source><![CDATA[<settings>
207-
<setting name="defaultScriptingLanguage" value="raw"/>
189+
<p>You can plug a language by implementing the following interface:</p>
190+
<source><![CDATA[public interface LanguageDriver {
191+
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
192+
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
193+
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
194+
}]]></source>
195+
<p>Once you have your custom language driver you can set it to be the default by configuring it in the mybatis-config.xml file:</p>
196+
<source><![CDATA[<typeAliases>
197+
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
198+
</typeAliases>
199+
<settings>
200+
<setting name="defaultScriptingLanguage" value="myLanguage"/>
208201
</settings>
209202
]]></source>
210-
<p>You can also specify the language you want to use in an specific statement by adding the <code>lang</code> attribute as follows:
203+
<p>Instead of changing the default, you can specify the language for an specific statement by adding the <code>lang</code> attribute as follows:
211204
</p>
212-
<source><![CDATA[<select id="selectBlog" lang="raw">
205+
<source><![CDATA[<select id="selectBlog" lang="myLanguage">
213206
SELECT * FROM BLOG
214207
</select>]]></source>
215208
<p>Or, in the case you are using mappers, using the <code>@Lang</code> annotation:</p>
216209
<source><![CDATA[public interface Mapper {
217-
@Lang(RawLanguageDriver.class)
210+
@Lang(MyLanguageDriver.class)
218211
@Select("SELECT * FROM BLOG")
219212
List<Blog> selectBlog();
220213
}]]></source>
221214

222215
<p><span class="label important">NOTE</span> You can use Apache Velocity as your dynamic language. Have a look at the MyBatis-Velocity project for the details.</p>
223216

224-
<p>You can also implement your own language driver by implementing the following interface:</p>
225-
<source><![CDATA[public interface LanguageDriver {
226-
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
227-
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
228-
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
229-
}]]></source>
217+
<p>All the xml tags you have seen in the previous sections are provided by the default MyBatis language that is provided by the driver
218+
<code>org.apache.ibatis.scripting.xmltags.XmlLanguageDriver</code> which is aliased as <code>xml</code>.</p>
230219
</subsection>
231220
</section>
232221
</body>

src/test/java/org/apache/ibatis/submitted/complex_column/Person.xml

+6-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@
4141
FROM Person
4242
WHERE id = #{id,jdbcType=INTEGER}
4343
</select>
44-
<select id="getParentWithoutComplex" resultMap="personMap" parameterType="Person">
44+
<!-- parameterType was removed because it failed when using RAW.
45+
the DynamicSqlSource calculates the parameter out of the actual parameter used in the call
46+
the RawSqlSource calculates it during startup.
47+
This paremter is a Person when called directly but an Integer when calling from the ResultMap (nested select)
48+
so it fails -->
49+
<select id="getParentWithoutComplex" resultMap="personMap">
4550
SELECT id, firstName, lastName, parent_id, parent_firstName, parent_lastName
4651
FROM Person
4752
WHERE id = #{id,jdbcType=INTEGER}

0 commit comments

Comments
 (0)