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
3131using the template(or natural template) mechanism provided by Thymeleaf 3.
3232If 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
4040The 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
115138The mybatis-thymeleaf provide following features using class that implements
116139the link:{mybatis-doc-url}/dynamic-sql.html#Pluggable_Scripting_Languages_For_Dynamic_SQL[`LanguageDriver` interface^]
117140for 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----
348371Configuration configuration = new Configuration();
349372configuration.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----
438471TemplateEngine templateEngine = new TemplateEngine(); // <1>
439472templateEngine.addDialect(new MyBatisDialect());
473+ templateEngine.setEngineContextFactory(new MyBatisDelegatingEngineContextFactory(
474+ targetTemplateEngine.getEngineContextFactory()));
440475// ...
441476
442477Configuration configuration = new Configuration();
443- configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(templateEngine)); // <2>
478+ configuration.getLanguageRegistry()
479+ .register(ThymeleafLanguageDriver.newBuilder().templateEngine(templateEngine).build()); // <2>
444480configuration.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
787823SELECT * 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
894931The 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 = ~
13031514dialect.like.escape-clause-format = escape '%s'
13041515dialect.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+ ====
0 commit comments