Skip to content

Commit dca0efe

Browse files
committed
Separate sql generating feature as new class
In this commit, includes following changes: * Mark the optional at MyBatis core module in pom.xml * Support MyBatis and Spring JDBC as built-in bind variable format * Upgrade to jacoco 0.8.5 Fixes mybatisgh-16
1 parent 57d9fa8 commit dca0efe

22 files changed

+2276
-593
lines changed

pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,15 @@
7979
<clirr.comparisonVersion>1.0.0</clirr.comparisonVersion>
8080

8181
<!-- Remove after parent 32 (support for jdk 13) -->
82-
<jacoco.version>0.8.4</jacoco.version>
82+
<jacoco.version>0.8.5</jacoco.version>
8383
</properties>
8484

8585
<dependencies>
8686
<dependency>
8787
<groupId>org.mybatis</groupId>
8888
<artifactId>mybatis</artifactId>
8989
<version>${mybatis.version}</version>
90+
<optional>true</optional>
9091
<scope>provided</scope>
9192
</dependency>
9293
<dependency>
@@ -119,6 +120,12 @@
119120
<version>1.2.3</version>
120121
<scope>test</scope>
121122
</dependency>
123+
<dependency>
124+
<groupId>org.springframework</groupId>
125+
<artifactId>spring-jdbc</artifactId>
126+
<version>5.2.1.RELEASE</version>
127+
<scope>test</scope>
128+
</dependency>
122129
</dependencies>
123130

124131
<build>

src/main/asciidoc/user-guide.adoc

Lines changed: 213 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ for integrating with template engine provide by Thymeleaf.
101101
* Can read an SQL template from a Thymeleaf template file on classpath
102102
* Can use a custom dialect(attribute tag and expression utility method) on your SQL template
103103
* Can fully customize a template engine configuration
104-
104+
* Can generate the SQL from SQL template without the MyBatis core module (since 1.0.2)
105105

106106
== Requirements
107107

@@ -743,7 +743,7 @@ configuration.setVariables(variables);
743743
[source,sql]
744744
.SQL template
745745
----
746-
SELECT * FROM /*[(${tableNameOfUser} ?: 'users')]*/ users -- <2>
746+
SELECT * FROM /*[# th:utext="${tableNameOfUser} ?: 'users'"]*/ users /*[/]*/ -- <2>
747747
----
748748

749749
<1> Define an any property as MyBatis's configuration properties
@@ -783,17 +783,16 @@ This configuration is optional. The non 2-way SQL can be use on the 2-way SQL mo
783783
use2way = false # <1>
784784
----
785785

786-
or
786+
<1> Set the `use2way` to `false`
787787

788788
[source,java]
789789
.How to configure using config class
790790
----
791791
configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(
792-
ThymeleafLanguageDriverConfig.newInstance(c -> c.setUse2Way(false)))); // <2>
792+
ThymeleafLanguageDriverConfig.newInstance(c -> c.setUse2Way(false)))); // <1>
793793
----
794794

795-
<1> Set the `use2way` to `false`
796-
<2> Set the `use2way` property to `false`
795+
<1> Set the `use2way` property of `ThymeleafLanguageDriverConfig` to `false`
797796

798797

799798
=== Basic usage
@@ -990,6 +989,169 @@ using <<Configuration properties, Configuration properties>>.
990989
AND firstName LIKE #{patternFirstName} ESCAPE '\'
991990
----
992991

992+
== Using SQL Generator
993+
994+
Since 1.0.2, we separate the SQL generating feature from the `ThymeleafLanguageDriver` and `ThymeleafSqlSource` class,
995+
we added the `SqlGenerator` and `SqlGeneratorConfig` for generating SQL from SQL template.
996+
These classes does not depends on the MyBatis core module(`mybatis-3.x.x.jar`).
997+
So that, it also can be used in combination with any data access libraries(e.g. Spring JDBC, JPA, R2DBC, etc...) that provide with named parameter.
998+
999+
=== Configuration
1000+
1001+
By default, the `SqlGenerator` applies settings for using together with the MyBatis core module(apply to `#{...}` as the bind variable format),
1002+
but you can customize a default settings using configuration properties file or the `SqlGeneratorConfig`.
1003+
The `SqlGeneratorConfig` allows the same configurations as the `ThymeleafLanguageDriverConfig` except the `TemplateFilePathProvider`(`template-file.path-provider.*`).
1004+
1005+
==== Customize the bind variable format
1006+
1007+
You can customize the bind variable format using configuration properties file or configuration class.
1008+
In the following example, it changes the bind variable format to the Spring JDBC format(e.g. `:id`) from MyBatis core format(e.g. `#{id}`).
1009+
1010+
[source,properties]
1011+
.How to customize using configuration properties file
1012+
----
1013+
dialect.bind-variable-render = org.mybatis.scripting.thymeleaf.processor.SpringJdbcBindVariableRender # <1>
1014+
----
1015+
1016+
<1> Specify the `BindVariableRender` implementation class(built-in class) that render Spring JDBC bind variable format
1017+
1018+
1019+
[source,java]
1020+
.How to customize using config class
1021+
----
1022+
SqlGeneratorConfig config = SqlGeneratorConfig.newInstanceWithCustomizer(c ->
1023+
c.getDialect().setBindVariableRender(BindVariableRender.BuiltIn.SPRING_JDBC.getType())); // <1>
1024+
SqlGenerator sqlGenerator = new SqlGenerator(config); // <2>
1025+
----
1026+
1027+
<1> Specify the `BindVariableRender` implementation class(built-in class) that render Spring JDBC bind variable format via `BuiltIn` enum
1028+
<2> Create a `SqlGenerator` instance with user defined configuration
1029+
1030+
If you use the custom bind variable format other than built-in format,
1031+
please create a implementation class of `BindVariableRender` and apply it to the configuration.
1032+
1033+
[source,java]
1034+
.How to create the BindVariableRender implementation class
1035+
----
1036+
public class R2dbcMySQLBindVariableRender extends EnclosingBasedBindVariableRender { // <1>
1037+
public R2dbcMySQLBindVariableRender() {
1038+
super("?", ""); // Render '?...' (e.g. ?id)
1039+
}
1040+
}
1041+
----
1042+
1043+
<1> Create a `BindVariableRender` implementation class(The `EnclosingBasedBindVariableRender` supports to create an implementation class)
1044+
1045+
1046+
==== Customize other configurations
1047+
1048+
Please see also the following sections.
1049+
1050+
* <<_customizing_configuration>>
1051+
1052+
1053+
=== Basic Usage
1054+
1055+
The `SqlGenerator` provide feature for generating a SQL from SQL template using the Thymeleaf as follow:
1056+
1057+
[source,java]
1058+
.Basic Usage:
1059+
----
1060+
SqlGenerator sqlGenerator = new SqlGenerator(); // <1>
1061+
1062+
Conditions conditions = new Conditions();
1063+
conditions.setId(10);
1064+
1065+
// sql = "SELECT * FROM accounts WHERE id = #{id}"
1066+
String sql = sqlGenerator.generate(
1067+
"SELECT * FROM accounts WHERE id = /*[# mb:p='id']*/ 1 /*[/]*/", conditions); // <2>
1068+
----
1069+
1070+
<1> Create a default instance of `SqlGenerator`
1071+
<2> Generate an SQL from SQL template
1072+
1073+
==== Specifying custom variables
1074+
1075+
You can specify any custom variables separately from the parameter object as follow:
1076+
1077+
[source,java]
1078+
.How to use custom variables:
1079+
----
1080+
SqlGenerator sqlGenerator = new SqlGenerator();
1081+
sqlGenerator.setDefaultCustomVariables(
1082+
Collections.singletonMap("accountsTableName", "users")); // <1>
1083+
1084+
Map<String, Object> accountMap = new HashMap<>();
1085+
accountMap.put("name", "Taro Yamada");
1086+
1087+
Map<String, Object> customVariables = new HashMap<>(); // <2>
1088+
customVariables.put("now", LocalDateTime.now());
1089+
customVariables.put("loginId", loginId);
1090+
1091+
// sql = "INSERT INTO users (name, created_at, created_by) VALUES(#{name}, #{now}, #{loginId})"
1092+
String sql = sqlGenerator.generate(
1093+
"INSERT INTO /*[# th:utext=\"${accountsTableName} ?: 'accounts'\"]*/ accounts /*[/]*/ " + // <3>
1094+
"(name, created_at, created_by) VALUES(" +
1095+
"/*[# mb:p='name']*/ 'Hanako Yamada' /*[/]*/, " +
1096+
"/*[# mb:p='now']*/ current_timestamp() /*[/]*/, " + // <4>
1097+
"/*[# mb:p='loginId']*/ 'A00000001' /*[/]*/" + // <4>
1098+
")", accountMap, customVariables); // <5>
1099+
----
1100+
1101+
<1> Specify the default custom variable for sharing by every statement
1102+
<2> Create custom variable per statement or transaction
1103+
<3> Can be replace to custom variable value at template processing time
1104+
<4> Can be bind a custom variable
1105+
<5> Specify(Pass) custom variables to sql generator at 3rd argument of `generate` method
1106+
1107+
1108+
==== Receiving custom bind variables
1109+
1110+
You can receiving custom bind variables that created during template processing via user define `Map` reference as follow:
1111+
1112+
[NOTE]
1113+
====
1114+
The custom bind variables may create when use `mb:bind` or `mb:p` tag.
1115+
====
1116+
1117+
[source,java]
1118+
.How to use custom bind variables store:
1119+
----
1120+
SqlGenerator sqlGenerator = new SqlGenerator();
1121+
1122+
Map<String, Object> conditionsMap = new HashMap<>();
1123+
conditionsMap.put("name", "Yamada");
1124+
1125+
// sql = "SELECT * FROM accounts WHERE name = #{patternName}"
1126+
// customBindVariablesStore = {"patternName":"Yamada%"}
1127+
Map<String, Object> customBindVariablesStore = new HashMap<>(); // <1>
1128+
String sql = sqlGenerator.generate(
1129+
"/*[# mb:bind='patternName=|${#likes.escapeWildcard(name)}%|' /]*/" +
1130+
"SELECT * FROM accounts WHERE name = /*[# mb:p='patternName']*/ 'Sato' /*[/]*/",
1131+
conditionsMap, null, customBindVariablesStore); // <2>
1132+
----
1133+
1134+
<1> Define a `Map` reference for receiving custom bind variables
1135+
<2> Specify(Pass) a `Map` reference for receiving custom bind variables at 4th argument of `generate` method
1136+
1137+
=== Advanced Usage
1138+
1139+
==== Access JavaBeans property
1140+
1141+
By default, the `SqlGenerator` use the JDK standard APIs(JavaBeans and Reflection API) for accessing a property of user defined Java object.
1142+
If there is a conflict with property accessing in the data access library,
1143+
you can change a default behavior by applying a custom `org.mybatis.scripting.thymeleaf.PropertyAccessor` implementation class.
1144+
1145+
[source,java]
1146+
.How to apply a custom PropertyAccessor
1147+
----
1148+
SqlGenerator sqlGenerator = new SqlGenerator();
1149+
sqlGenerator.setPropertyAccessor(new MyPropertyAccessor()); // <1>
1150+
----
1151+
1152+
<1> Set a custom `PropertyAccessor` implementation class to the `SqlGenerator`
1153+
1154+
9931155
== Support classes
9941156

9951157
We provides useful classes for supporting development.
@@ -1076,7 +1238,7 @@ If you specify the `ESCAPE '\'` directly as static template parts, the Thymeleaf
10761238
.Invalid usage
10771239
----
10781240
/*[# mb:bind="patternFirstName=|${#likes.escapeWildcard(firstName)}%|" /]*/
1079-
AND firstName LIKE /*[('#{patternFirstName}')]*/ 'Taro%' /**/ ESCAPE '\'
1241+
AND firstName LIKE /*[('#{patternFirstName}')]*/ 'Taro%' /**/ ESCAPE '\' --<1>
10801242
----
10811243

10821244
<1> Specify the `ESCAPE '\'` directly as static template parts
@@ -1145,7 +1307,8 @@ The mybatis-thymeleaf provides following properties for customizing configuratio
11451307
|`String[]`
11461308
|`"*.sql"`
11471309

1148-
4+|*Template file path provider configuration(for TemplateFilePathProvider)*
1310+
4+|*Template file path provider configuration for TemplateFilePathProvider* +
1311+
(Available only at `ThymeleafLanguageDriverConfig`)
11491312

11501313
|`template-file.path-provider.prefix`
11511314
|The prefix for adding to template file path
@@ -1195,6 +1358,13 @@ The mybatis-thymeleaf provides following properties for customizing configuratio
11951358
(Can specify multiple characters using comma(`","`) as separator character)
11961359
|`Character[]`
11971360
|`""` (no specify)
1361+
1362+
|`dialect.bind-variable-render`
1363+
|The FQCN of class that implements the `BindVariableRender`
1364+
(interface for rendering a bind variable ( such as `#{id}`, `:id`, etc...)
1365+
|The `BindVariableRender` implementation class for rendering bind variable
1366+
|`Class`
1367+
|`org.mybatis.scripting.thymeleaf.processor.MyBatisBindVariableRender` (no specify)
11981368
|===
11991369

12001370
[source,properties]
@@ -1215,6 +1385,7 @@ dialect.prefix = mybatis
12151385
dialect.like-escape-char = ~
12161386
dialect.like-escape-clause-format = escape '%s'
12171387
dialect.like-additional-escape-target-chars = %, _
1388+
dialect.bind-variable-render = org.mybatis.scripting.thymeleaf.processor.SpringJdbcBindVariableRender
12181389
----
12191390

12201391
[TIP]
@@ -1235,11 +1406,14 @@ configuration.getLanguageRegistry().register(
12351406
c.getTemplateFile().getPathProvider().setPrefix("sqls/");
12361407
c.getTemplateFile().getPathProvider().setIncludesPackagePath(false);
12371408
c.getTemplateFile().getPathProvider().setSeparateDirectoryPerMapper(false);
1238-
c.getTemplateFile().getPathProvider().setIncludesMapperNameWhenSeparateDirectory(false);
1409+
c.getTemplateFile().getPathProvider()
1410+
.setIncludesMapperNameWhenSeparateDirectory(false);
12391411
c.getDialect().setPrefix("mybatis");
12401412
c.getDialect().setLikeEscapeChar('~');
12411413
c.getDialect().setLikeEscapeClauseFormat("escape '%s'");
12421414
c.getDialect().setLikeAdditionalEscapeTargetChars('%', '_');
1415+
c.getDialect().setBindVariableRender(
1416+
BindVariableRender.BuiltIn.SPRING_JDBC.getType());
12431417
})));
12441418
----
12451419
@@ -1251,6 +1425,36 @@ We provide following factory methods for creating a `ThymeleafLanguageDriver` in
12511425
* `newInstance(Properties customProperties)`
12521426
* `newInstance(Consumer<ThymeleafLanguageDriverConfig> customizer)`
12531427
1428+
These properties can be specified via factory method of `SqlGeneratorConfig` as follow:
1429+
1430+
[source,java]
1431+
----
1432+
SqlGeneratorConfig config =
1433+
SqlGeneratorConfig.newInstanceWithCustomizer(c -> {
1434+
c.setUse2way(false);
1435+
c.setCustomizer(CustomTemplateEngineCustomizer.class);
1436+
c.getTemplateFile().setCacheEnabled(false);
1437+
c.getTemplateFile().setCacheTtl(3600000L);
1438+
c.getTemplateFile().setEncoding(StandardCharsets.UTF_8);
1439+
c.getTemplateFile().setBaseDir("templates/");
1440+
c.getTemplateFile().setPatterns("*.sql", "*.sql.template");
1441+
c.getDialect().setPrefix("mybatis");
1442+
c.getDialect().setLikeEscapeChar('~');
1443+
c.getDialect().setLikeEscapeClauseFormat("escape '%s'");
1444+
c.getDialect().setLikeAdditionalEscapeTargetChars('%', '_');
1445+
c.getDialect().setBindVariableRender(
1446+
BindVariableRender.BuiltIn.SPRING_JDBC.getType());
1447+
});
1448+
// ...
1449+
----
1450+
1451+
We provide following factory methods for creating a `SqlGeneratorConfig` instance.
1452+
1453+
* `newInstance()`
1454+
* `newInstanceWithResourcePath(String resourcePath)`
1455+
* `newInstanceWithProperties(Properties customProperties)`
1456+
* `newInstanceWithCustomizer(Consumer<SqlGeneratorConfig> customizer)`
1457+
12541458
====
12551459

12561460

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import java.util.Arrays;
1919
import java.util.Collections;
2020
import java.util.HashSet;
21+
import java.util.Optional;
2122
import java.util.Set;
2223

2324
import org.mybatis.scripting.thymeleaf.expression.Likes;
25+
import org.mybatis.scripting.thymeleaf.processor.BindVariableRender;
2426
import org.mybatis.scripting.thymeleaf.processor.MyBatisBindTagProcessor;
2527
import org.mybatis.scripting.thymeleaf.processor.MyBatisParamTagProcessor;
2628
import org.thymeleaf.context.IExpressionContext;
@@ -50,6 +52,8 @@ public class MyBatisDialect extends AbstractProcessorDialect implements IExpress
5052

5153
private Likes likes = Likes.newBuilder().build();
5254

55+
private BindVariableRender bindVariableRender;
56+
5357
/**
5458
* Default constructor.
5559
*/
@@ -77,15 +81,31 @@ public void setLikes(Likes likes) {
7781
this.likes = likes;
7882
}
7983

84+
/**
85+
* Set a bind variable render.
86+
*
87+
* @param bindVariableRender
88+
* a bind variable render
89+
* @since 1.0.2
90+
*/
91+
public void setBindVariableRender(BindVariableRender bindVariableRender) {
92+
this.bindVariableRender = bindVariableRender;
93+
}
94+
8095
/**
8196
* {@inheritDoc}
8297
*/
8398
@Override
8499
public Set<IProcessor> getProcessors(String dialectPrefix) {
85100
return new HashSet<>(Arrays.asList(new MyBatisBindTagProcessor(TemplateMode.TEXT, dialectPrefix),
86101
new MyBatisBindTagProcessor(TemplateMode.CSS, dialectPrefix),
87-
new MyBatisParamTagProcessor(TemplateMode.TEXT, dialectPrefix),
88-
new MyBatisParamTagProcessor(TemplateMode.CSS, dialectPrefix)));
102+
configure(new MyBatisParamTagProcessor(TemplateMode.TEXT, dialectPrefix)),
103+
configure(new MyBatisParamTagProcessor(TemplateMode.CSS, dialectPrefix))));
104+
}
105+
106+
private MyBatisParamTagProcessor configure(MyBatisParamTagProcessor processor) {
107+
Optional.ofNullable(bindVariableRender).ifPresent(processor::setBindVariableRender);
108+
return processor;
89109
}
90110

91111
/**

0 commit comments

Comments
 (0)