Skip to content

Commit c6a9116

Browse files
committed
Add tests for non 2-way SQL
1 parent 8d1d33f commit c6a9116

File tree

18 files changed

+1117
-61
lines changed

18 files changed

+1117
-61
lines changed

src/main/asciidoc/user-guide.adoc

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

2828
=== What is MyBatis Thymeleaf ?
2929

30-
The mybatis-thymeleaf is a plugin that helps applying the dynamic sql and 2-way SQL feature to the MyBatis 3
30+
The mybatis-thymeleaf is a plugin that helps applying the 2-way SQL/dynamic SQL feature to the MyBatis 3
3131
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^]
3535
* {thymeleaf-doc-url}/usingthymeleaf.html[Tutorial: Using Thymeleaf^]
3636
* {thymeleaf-doc-url}/usingthymeleaf.html#textual-template-modes[Tutorial: Using Thymeleaf -13 Textual template modes-^]
3737

38-
=== What is 2-way SQL ?
38+
=== What is 2-way SQL/Dynamic SQL ?
3939

4040
The 2-way SQL can be used by following *two way*.
4141

@@ -110,14 +110,37 @@ SELECT * FROM names
110110
ORDER BY id
111111
----
112112

113+
==== Dynamically bindable SQL
114+
115+
The mybatis-thymeleaf translate a dynamically bindable SQL(non 2-way SQL) that specified by template to as follow:
116+
117+
[source,sql]
118+
.Template
119+
----
120+
SELECT * FROM names
121+
WHERE 1 = 1
122+
[# th:if="${not #lists.isEmpty(ids)}"]
123+
AND id IN (
124+
[# th:each="id : ${ids}"]
125+
[(${idStat.first} ? '' : ',')]
126+
[('#{ids[' + ${idStat.index} + ']}')]
127+
[/]
128+
)
129+
[/]
130+
ORDER BY id
131+
----
132+
133+
This template is simple compare with 2-way SQL, but it cannot execute as-is in SQL execution tool (such as psql, mysql, sqlplus, plugins for IDE, etc...).
134+
135+
113136
=== Mainly Features
114137

115138
The mybatis-thymeleaf provide following features using class that implements
116139
the link:{mybatis-doc-url}/dynamic-sql.html#Pluggable_Scripting_Languages_For_Dynamic_SQL[`LanguageDriver` interface^]
117140
for integrating with template engine provide by Thymeleaf.
118141

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
142+
* Can write 2-way SQL/dynamic SQL
143+
* Can use a 2-way SQL/dynamic SQL via an annotation and mapper xml
121144
* Can read an SQL template from a template file on classpath
122145
* Can use a custom dialect(attribute tag and expression utility method) at a template
123146
* Can fully customize a template engine configuration
@@ -347,6 +370,8 @@ You configure to use the `org.mybatis.scripting.thymeleaf.ThymeleafLanguageDrive
347370
----
348371
Configuration configuration = new Configuration();
349372
configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); // <1>
373+
// ...
374+
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
350375
----
351376

352377
<1> Set the `ThymeleafLanguageDriver` class to a `Configuration` instance as default scripting language driver
@@ -362,6 +387,14 @@ configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); // <1>
362387
</settings>
363388
----
364389

390+
[source,java]
391+
----
392+
SqlSessionFactory sqlSessionFactory;
393+
try (Reader configReader = Resources.getResourceAsReader("mybatis-config.xml")) {
394+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configReader);
395+
}
396+
----
397+
365398
<1> Set the `ThymeleafLanguageDriver` class to the `defaultScriptingLanguage` of setting item in configuration XML file
366399

367400
=== Customizing configuration
@@ -437,10 +470,13 @@ you can apply a user-defined template engine(full managed template engine) to th
437470
----
438471
TemplateEngine templateEngine = new TemplateEngine(); // <1>
439472
templateEngine.addDialect(new MyBatisDialect());
473+
templateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(
474+
targetTemplateEngine.getEngineContextFactory()));
440475
// ...
441476
442477
Configuration configuration = new Configuration();
443-
configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(templateEngine)); // <2>
478+
configuration.getLanguageRegistry()
479+
.register(ThymeleafLanguageDriver.newBuilder().templateEngine(templateEngine).build()); // <2>
444480
configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); // <3>
445481
----
446482

@@ -606,7 +642,7 @@ SELECT * FROM names
606642
----
607643

608644
<1> Can use the `#mybatis.commaIfNotFirst(IterationStatusVar)` method instead of standard dialect
609-
<1> Can use the `#mybatis.inClauseBindVariables(String, int)` method instead of standard dialect or above utility method
645+
<2> Can use the `#mybatis.inClauseBindVariables(String, int)` method instead of standard dialect or above utility method
610646

611647

612648
=== Fragment
@@ -787,6 +823,7 @@ SELECT * FROM accounts
787823
SELECT * FROM users
788824
----
789825

826+
790827
== Cautions for usage
791828

792829
[CAUTION]
@@ -866,8 +903,8 @@ UPDATE names
866903

867904
=== Using '\'(backslash)
868905

869-
If you are using 2-way SQL format, there is case that cannot parse a 2-way SQL when specify `'\'`(backslash) within static template parts.
870-
We know that following case cannot be parsed 2-way SQL.
906+
If you are using 2-way SQL mode, there is case that cannot parse a 2-way SQL when specify `'\'`(backslash) within static template parts.
907+
We know that following case cannot be parsed 2-way SQL. If you are not using 2-way SQL mode, this limitation can be ignore.
871908

872909
==== ESCAPE clause for LIKE
873910

@@ -892,7 +929,7 @@ For detail, please see <<likeEscapeClause>>.
892929
== Custom Dialect
893930

894931
The mybatis-thymeleaf provide the custom dialect class(`org.mybatis.scripting.thymeleaf.MyBatisDialect`)
895-
that help for generating SQL.
932+
that help for generating dynamic SQL.
896933

897934
=== Attribute tag
898935

@@ -1163,6 +1200,177 @@ id IN (
11631200
AND firstName LIKE #{patternFirstName} ESCAPE '\'
11641201
----
11651202

1203+
== Using non 2-way SQL mode
1204+
1205+
The non 2-way SQL is simple a little compare with 2-way SQL and limitations not found at now.
1206+
1207+
=== Configuration
1208+
1209+
By default, the mybatis-thymeleaf will be use the 2-way SQL mode.
1210+
Therefore you should be configure explicitly to use the non 2-way SQL mode using configuration properties file or builder option as follow:
1211+
1212+
[source,properties]
1213+
.How to configure using configuration properties file(src/main/resources/mybatis-thymeleaf.properties)
1214+
----
1215+
use-2way = false # <1>
1216+
----
1217+
1218+
<1> Set the `use-2way` to `false`
1219+
1220+
[source,java]
1221+
.How to configure using builder option
1222+
----
1223+
// ...
1224+
configuration.getLanguageRegistry()
1225+
.register(ThymeleafLanguageDriver.newBuilder().use2way(false).build()); // <1>
1226+
// ...
1227+
----
1228+
1229+
<1> Set the `use2way` builder option to `false`
1230+
1231+
1232+
=== Binding value
1233+
1234+
In the non 2-way SQL mode, you use the MyBatis's standard bind variable expression such as `#{...}` as follow:
1235+
1236+
[source,sql]
1237+
.MyBatis's standard bind variable expression
1238+
----
1239+
SELECT * FROM names WHERE id = #{id}
1240+
----
1241+
1242+
If you bind a iteration object(List etc..) using dynamic SQL,
1243+
you need to generate MyBatis's standard bind variable expression using inlined expression of Thymeleaf as follow:
1244+
1245+
[source,sql]
1246+
.Template
1247+
----
1248+
SELECT * FROM names
1249+
WHERE 1 = 1
1250+
[# th:if="${not #lists.isEmpty(ids)}"]
1251+
AND id IN (
1252+
[# th:each="id : ${ids}"]
1253+
[(${idStat.first} ? '' : ',')]
1254+
[('#{ids[' + ${idStat.index} + ']}')] -- <1>
1255+
[/]
1256+
)
1257+
[/]
1258+
ORDER BY id
1259+
----
1260+
1261+
<1> Generate bind variable expression.
1262+
1263+
An above template will be translate to following SQL in Thymeleaf processing.
1264+
1265+
[source,sql]
1266+
.Translated to SQL (when `ids` has 3 elements)
1267+
----
1268+
SELECT * FROM names
1269+
WHERE 1 = 1
1270+
AND id IN (
1271+
#{ids[0]}
1272+
,
1273+
#{ids[1]}
1274+
,
1275+
#{ids[2]}
1276+
)
1277+
ORDER BY id
1278+
----
1279+
1280+
=== Different with 2-way SQL mode
1281+
1282+
The different with 2-way SQL mode is that will be unnecessary to enclose the thymeleaf expressions as SQL comment(`/\*[...]*/`).
1283+
In this section, some samples are provide the non 2-way SQL.
1284+
1285+
==== Using tag for specifying condition
1286+
1287+
If you add a SQL part when any condition is matches or not, you can use following attribute tags (`th:if`, `th:unless`, `th:switch` and `th:case`).
1288+
1289+
[source,sql]
1290+
.Usage of conditional attribute tag on WHERE
1291+
----
1292+
SELECT * FROM names
1293+
WHERE 1 = 1
1294+
[# th:if="${firstName} != null"]
1295+
AND firstName = #{firstName}
1296+
[/]
1297+
ORDER BY id
1298+
----
1299+
1300+
[source,sql]
1301+
.Usage of conditional attribute tag on SET
1302+
----
1303+
UPDATE names
1304+
SET id = id
1305+
[# th:if="${firstName} != null"]
1306+
, firstName = #{firstName}
1307+
[/]
1308+
WHERE id = #{id}
1309+
----
1310+
1311+
==== Using tag for iteration
1312+
1313+
The Thymeleaf supports to process for iteration object(`List` etc..) using `th:each`.
1314+
1315+
[source,sql]
1316+
.Usage of iteration
1317+
----
1318+
SELECT * FROM names
1319+
WHERE 1 = 1
1320+
[# th:if="${not #lists.isEmpty(ids)}"]
1321+
AND id IN (
1322+
[# th:each="id : ${ids}"]
1323+
[(${idStat.first} ? '' : ',')]
1324+
[('#{ids[' + ${idStat.index} + ']}')]
1325+
[/]
1326+
)
1327+
[/]
1328+
ORDER BY id
1329+
----
1330+
1331+
[source,sql]
1332+
.Use the custom expression utility method for appending comma
1333+
----
1334+
SELECT * FROM names
1335+
WHERE 1 = 1
1336+
/*[# th:if="${not #lists.isEmpty(ids)}"]*/
1337+
AND id IN (
1338+
/*[# th:each="id : ${ids}"]*/
1339+
/*[(${#mybatis.commaIfNotFirst(idStat)})]*/ -- <1>
1340+
/*[('#{ids[' + ${idStat.index} + ']}')]*/ 1
1341+
/*[/]*/
1342+
)
1343+
/*[/]*/
1344+
ORDER BY id
1345+
----
1346+
1347+
==== Using custom expression utility method
1348+
1349+
[source,sql]
1350+
.Use the custom expression utility method for creating bind variables string of IN clause
1351+
----
1352+
SELECT * FROM names
1353+
WHERE 1 = 1
1354+
[# th:if="${not #lists.isEmpty(ids)}"]
1355+
AND id IN (
1356+
[(${#mybatis.inClauseBindVariables('ids', ids.size())})]
1357+
)
1358+
[/]
1359+
ORDER BY id
1360+
----
1361+
1362+
==== Using custom attribute tag
1363+
1364+
[source,sql]
1365+
.Use the custom attribute tag
1366+
----
1367+
SELECT * FROM names
1368+
WHERE 1 = 1
1369+
[# th:if="${firstName} != null"]
1370+
[# mybatis:bind="patternFirstName=|${#mybatis.escapeLikeWildcard(firstName)}%|" /]
1371+
AND firstName LIKE #{patternFirstName}
1372+
[/]
1373+
----
11661374

11671375
== Usage on framework
11681376

@@ -1197,9 +1405,11 @@ ConfigurationCustomizer mybatisConfigurationCustomizer() {
11971405
return configuration -> {
11981406
TemplateEngine templateEngine = new TemplateEngine(); // <1>
11991407
templateEngine.addDialect(new MyBatisDialect());
1200-
templateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(targetTemplateEngine.getEngineContextFactory()));
1408+
templateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(
1409+
targetTemplateEngine.getEngineContextFactory()));
12011410
// ...
1202-
configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(templateEngine)); // <2>
1411+
configuration.getLanguageRegistry().register(
1412+
ThymeleafLanguageDriver.newBuilder().templateEngine(templateEngine).build()); // <2>
12031413
configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); // <3>
12041414
};
12051415
}
@@ -1212,6 +1422,7 @@ ConfigurationCustomizer mybatisConfigurationCustomizer() {
12121422
<3> Set the `ThymeleafLanguageDriver` class as default scripting language driver instead of
12131423
specifying as configuration properties
12141424

1425+
12151426
== Appendix
12161427

12171428
=== Configuration properties
@@ -1303,3 +1514,18 @@ dialect.like.escape-char = ~
13031514
dialect.like.escape-clause-format = escape '%s'
13041515
dialect.like.additional-escape-target-chars = %, _
13051516
----
1517+
1518+
[TIP]
1519+
====
1520+
These properties can be specified via builder class of `ThymeleafLanguageDriver` as follow:
1521+
1522+
[source,java]
1523+
----
1524+
// ...
1525+
configuration.getLanguageRegistry()
1526+
.register(ThymeleafLanguageDriver.newBuilder().use2way(false).build());
1527+
// ...
1528+
----
1529+
1530+
If you specify the value both with properties file and builder option, the properties file value applied.
1531+
====

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ private ContextVariableNames() {
3636

3737
/**
3838
* Variable name for holding whether use fallback parameter object when parameter is value object.
39+
*
40+
* @see org.mybatis.scripting.thymeleaf.MyBatisDelegatingEngineContextFactory
3941
*/
4042
public static final String FALLBACK_PARAMETER_OBJECT = "_fallbackParameterObject";
4143

0 commit comments

Comments
 (0)