Skip to content

Commit 8d1d33f

Browse files
committed
Allow any variable name on ognl expression when specify single value object as parameter object
1 parent 7f61ab0 commit 8d1d33f

File tree

7 files changed

+110
-16
lines changed

7 files changed

+110
-16
lines changed

src/main/asciidoc/user-guide.adoc

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727

2828
=== What is MyBatis Thymeleaf ?
2929

30-
The mybatis-thymeleaf is a plugin that helps applying a 2-way SQL feature to the MyBatis 3
31-
using the natural template mechanism provided by Thymeleaf 3.
30+
The mybatis-thymeleaf is a plugin that helps applying the dynamic sql and 2-way SQL feature to the MyBatis 3
31+
using the template(or natural template) mechanism provided by Thymeleaf 3.
3232
If you are not familiar with MyBatis and Thymeleaf, you can see following official documentations.
3333

3434
* {mybatis-doc-url}[MyBatis 3 REFERENCE DOCUMENTATION^]
@@ -54,12 +54,19 @@ SELECT * FROM names
5454
----
5555

5656
[source,sql]
57-
.Translated SQL for processing in MyBatis
57+
.Translated to SQL that can parse in MyBatis by Thymeleaf processing
5858
----
5959
SELECT * FROM names
6060
WHERE id = #{id}
6161
----
6262

63+
[source,sql]
64+
.Translated to SQL that can parse by JDBC driver in MyBatis processing
65+
----
66+
SELECT * FROM names
67+
WHERE id = ?
68+
----
69+
6370
==== Dynamically bindable 2-way SQL
6471

6572
The mybatis-thymeleaf translate a dynamically bindable 2-way SQL that specified by natural template to as follow:
@@ -81,15 +88,15 @@ SELECT * FROM names
8188
----
8289

8390
[source,sql]
84-
.Translated SQL for processing in MyBatis (when `ids` is empty)
91+
.Translated to SQL in Thymeleaf processing (when `ids` is empty)
8592
----
8693
SELECT * FROM names
8794
WHERE 1 = 1
8895
ORDER BY id
8996
----
9097

9198
[source,sql]
92-
.Translated SQL for processing in MyBatis (when `ids` has 3 elements)
99+
.Translated to SQL in Thymeleaf processing (when `ids` has 3 elements)
93100
----
94101
SELECT * FROM names
95102
WHERE 1 = 1
@@ -109,9 +116,9 @@ The mybatis-thymeleaf provide following features using class that implements
109116
the link:{mybatis-doc-url}/dynamic-sql.html#Pluggable_Scripting_Languages_For_Dynamic_SQL[`LanguageDriver` interface^]
110117
for integrating with template engine provide by Thymeleaf.
111118

112-
* Can write a 2-way SQL
113-
* Can use a 2-way SQL via an annotation and mapper xml
114-
* Can read a 2-way SQL from a template file on classpath
119+
* Can write a dynamic sql and 2-way SQL
120+
* Can use a dynamic sql and 2-way SQL via an annotation and mapper xml
121+
* Can read an SQL template from a template file on classpath
115122
* Can use a custom dialect(attribute tag and expression utility method) at a template
116123
* Can fully customize a template engine configuration
117124

@@ -138,7 +145,7 @@ About tested versions see the latest link:{travis-ci-url}[Travi CI^] build resul
138145
As basically policy, we do test using following versions.
139146
140147
* latest release version on 3.4.x line
141-
* latest release version on 3.5.x line (not released yet)
148+
* latest release version on 3.5.x line
142149
* latest snapshot version on 3.5.x line
143150
====
144151

@@ -260,7 +267,7 @@ public class NameMapper {
260267
[TIP]
261268
====
262269
263-
Since JDK 12, you can specify a 2-way SQL using "Raw String Literals" feature as follow:
270+
You can specify a 2-way SQL in annotation using "link:https://openjdk.java.net/jeps/326[Raw String Literals^]" feature that support by future JDK version as follow:
264271
265272
[source,java]
266273
----
@@ -474,7 +481,7 @@ that can be parsed by MyBatis core module as follow:
474481
.2-way SQL template for generating string that can be parsed by MyBatis core module
475482
----
476483
SELECT * FROM names
477-
WHERE id = /*[('#{id}')]*/ -- <1>
484+
WHERE id = /*[('#{id}')]*/ 1 -- <1>
478485
----
479486

480487
<1> A bind value specify by `/\*[('#{variable name}')]*/` format
@@ -722,6 +729,17 @@ Therefore, we *don't recommend* using this variable within the your application.
722729
====
723730

724731
|`Map<String, Object>`
732+
733+
|`_fallbackParameterObject`
734+
a|Whether can access by any variable name for value object that specified as parameter object.
735+
736+
[CAUTION]
737+
====
738+
This variable used by internal processing on the mybatis-thymeleaf.
739+
Therefore, we *don't recommend* using this variable within the your application.
740+
====
741+
742+
|`boolean`
725743
|===
726744

727745
=== Using configuration properties
@@ -1179,6 +1197,7 @@ ConfigurationCustomizer mybatisConfigurationCustomizer() {
11791197
return configuration -> {
11801198
TemplateEngine templateEngine = new TemplateEngine(); // <1>
11811199
templateEngine.addDialect(new MyBatisDialect());
1200+
templateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(targetTemplateEngine.getEngineContextFactory()));
11821201
// ...
11831202
configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(templateEngine)); // <2>
11841203
configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); // <3>

src/main/java/org/mybatis/scripting/thymeleaf/ContextVariableNames.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@ private ContextVariableNames() {
3434
*/
3535
public static final String CUSTOM_BIND_VARS = "_customBindVariables";
3636

37+
/**
38+
* Variable name for holding whether use fallback parameter object when parameter is value object.
39+
*/
40+
public static final String FALLBACK_PARAMETER_OBJECT = "_fallbackParameterObject";
41+
3742
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2018-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.scripting.thymeleaf;
17+
18+
import org.apache.ibatis.scripting.xmltags.DynamicContext;
19+
import org.thymeleaf.IEngineConfiguration;
20+
import org.thymeleaf.context.IContext;
21+
import org.thymeleaf.context.IEngineContext;
22+
import org.thymeleaf.context.IEngineContextFactory;
23+
import org.thymeleaf.engine.TemplateData;
24+
25+
import java.lang.reflect.Proxy;
26+
import java.util.Map;
27+
28+
/**
29+
*
30+
* @author Kazuki Shimizu
31+
* @version 1.0.0
32+
*/
33+
public class MyBatisDelegatingEngineContextFactory implements IEngineContextFactory {
34+
private final IEngineContextFactory delegate;
35+
private ClassLoader classLoader = MyBatisDelegatingEngineContextFactory.class.getClassLoader();
36+
37+
public MyBatisDelegatingEngineContextFactory(IEngineContextFactory delegate) {
38+
this.delegate = delegate;
39+
}
40+
41+
public void setClassLoader(ClassLoader classLoader) {
42+
this.classLoader = classLoader;
43+
}
44+
45+
@Override
46+
public IEngineContext createEngineContext(IEngineConfiguration configuration, TemplateData templateData, Map<String, Object> templateResolutionAttributes, IContext context) {
47+
IEngineContext engineContext = delegate.createEngineContext(configuration, templateData, templateResolutionAttributes, context);
48+
return (IEngineContext) Proxy.newProxyInstance(classLoader, new Class[]{IEngineContext.class}, (proxy, method, args) -> {
49+
if (method.getName().equals("getVariable")) {
50+
String name = (String) args[0];
51+
Object value;
52+
if (engineContext.containsVariable(ContextVariableNames.FALLBACK_PARAMETER_OBJECT) &&
53+
(boolean) engineContext.getVariable(ContextVariableNames.FALLBACK_PARAMETER_OBJECT)) {
54+
value = engineContext.containsVariable(name) ?
55+
engineContext.getVariable(name) : engineContext.getVariable(DynamicContext.PARAMETER_OBJECT_KEY);
56+
} else {
57+
value = engineContext.getVariable(name);
58+
}
59+
return value;
60+
}
61+
return method.invoke(engineContext, args);
62+
});
63+
}
64+
65+
}

src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriver.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ private ITemplateEngine createDefaultTemplateEngine() {
185185
targetTemplateEngine.addTemplateResolver(classLoaderTemplateResolver);
186186
targetTemplateEngine.addTemplateResolver(stringTemplateResolver);
187187
targetTemplateEngine.addDialect(dialect);
188+
targetTemplateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(targetTemplateEngine.getEngineContextFactory()));
188189

189190
customizer.accept(targetTemplateEngine);
190191
return targetTemplateEngine;

src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafSqlSource.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class ThymeleafSqlSource implements SqlSource {
7272
public BoundSql getBoundSql(Object parameterObject) {
7373
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
7474
DynamicContext dynamicContext = new DynamicContext(configuration, parameterObject);
75+
dynamicContext.bind(ContextVariableNames.FALLBACK_PARAMETER_OBJECT,
76+
parameterObject != null && configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass()));
7577

7678
CustomBindVariablesContext context;
7779
if (parameterObject instanceof Map) {
@@ -113,7 +115,7 @@ private AbstractCustomBindVariablesContext(DynamicContext dynamicContext, Proper
113115
this.variableNames = new HashSet<>();
114116
addVariableNames(dynamicContext.getBindings().keySet());
115117
Optional.ofNullable(configurationProperties).ifPresent(v -> addVariableNames(v.stringPropertyNames()));
116-
addVariableNames(Collections.singleton(ContextVariableNames.CUSTOM_BIND_VARS));
118+
addVariableNames(Collections.singletonList(ContextVariableNames.CUSTOM_BIND_VARS));
117119
}
118120

119121
void addVariableNames(Collection<String> names) {

src/test/resources/org/mybatis/scripting/thymeleaf/integrationtest/mapper/XmlNameSqlSessionMapper.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2018 the original author or authors.
4+
Copyright 2018-2019 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
</insert>
2828

2929
<update id="update">
30+
<![CDATA[
3031
UPDATE names
3132
SET id = id
3233
/*[# th:if="${firstName} != null"]*/
@@ -36,6 +37,7 @@
3637
,lastName = /*[('#{lastName}')]*/ 'Yamada'
3738
/*[/]*/
3839
WHERE id = /*[('#{id}')]*/ 1
40+
]]>
3941
</update>
4042

4143
<update id="updateWithEmptyComment">

src/test/resources/sql/NameMapper/findByIdWithoutParamAnnotation.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--
2-
-- Copyright 2018 the original author or authors.
2+
-- Copyright 2018-2019 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.
@@ -16,6 +16,6 @@
1616

1717
SELECT * FROM names
1818
WHERE 1 = 1
19-
/*[# th:if="${value} != null"]*/
20-
AND id = /*[('#{value}')]*/ 1
19+
/*[# th:if="${id} != null"]*/
20+
AND id = /*[('#{id}')]*/ 1
2121
/*[/]*/

0 commit comments

Comments
 (0)