Skip to content

Commit 55ae75c

Browse files
authored
Merge pull request #19 from kazuki43zoo/gh-10
Add SQL provider class that help detecting a template file automatically
2 parents 18cd409 + 3abeb07 commit 55ae75c

23 files changed

+990
-3
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ jdk:
66
- oraclejdk8
77

88
script:
9-
# build using mybatis latest version
9+
# build using mybatis latest released version
1010
- ./mvnw clean verify
11-
# build using mybatis 3.4.x line
12-
- ./mvnw clean verify -Dmybatis.version=3.4.6
11+
# test using mybatis 3.4.x line
12+
- ./mvnw test -Dmybatis.version=3.4.6
1313
# build using mybatis 3.5.x snapshot
1414
- ./mvnw clean verify -Dmybatis.version=3.5.2-SNAPSHOT
1515

pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@
117117
</dependencies>
118118

119119
<build>
120+
<pluginManagement>
121+
<plugins>
122+
<plugin>
123+
<artifactId>maven-surefire-plugin</artifactId>
124+
<configuration>
125+
<systemPropertyVariables>
126+
<mybatis.version>${mybatis.version}</mybatis.version>
127+
</systemPropertyVariables>
128+
</configuration>
129+
</plugin>
130+
</plugins>
131+
</pluginManagement>
120132
<plugins>
121133
<plugin>
122134
<groupId>org.asciidoctor</groupId>

src/main/asciidoc/user-guide.adoc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,76 @@ using <<Configuration properties, Configuration properties>>.
963963
AND firstName LIKE #{patternFirstName} ESCAPE '\'
964964
----
965965

966+
== Support classes
967+
968+
We provides useful classes for supporting development.
969+
970+
=== TemplateFilePathProvider
971+
972+
The `TemplateFilePathProvider` is SQL provider class that return the SQL template file path(Available since 1.0.1).
973+
This class use with SQL provider annotation(`@InsertProvider`, `@UpdateProvider`, `@DeleteProvider` and `@SelectProvider`}) as follow:
974+
975+
[NOTE]
976+
====
977+
**This class required to use on MyBatis 3.5.1+.**
978+
====
979+
980+
.Usage:
981+
982+
[source, java]
983+
----
984+
package com.example.mapper;
985+
986+
public interface BaseMapper<T> {
987+
988+
@Options(useGeneratedKeys = true, keyProperty = "id")
989+
@InsertProvider(type = TemplateFilePathProvider.class)
990+
void insert(T entity);
991+
992+
@UpdateProvider(type = TemplateFilePathProvider.class)
993+
void update(T entity);
994+
995+
@DeleteProvider(type = TemplateFilePathProvider.class)
996+
void delete(T entity);
997+
998+
@SelectProvider(type = TemplateFilePathProvider.class)
999+
T findById(Integer id);
1000+
1001+
}
1002+
----
1003+
1004+
[source, java]
1005+
----
1006+
package com.example.mapper;
1007+
1008+
public interface NameMapper extends BaseMapper {
1009+
1010+
@SelectProvider(type = TemplateFilePathProvider.class)
1011+
List<Name> findByCondition(NameCondition condition);
1012+
1013+
}
1014+
----
1015+
1016+
By default implementation, a template file path resolve following format and priority order.
1017+
If does not match all, it throw an exception that indicate not found a template file.
1018+
1019+
* `com/example/mapper/NameMapper/NameMapper-{methodName}-{databaseId}.sql`
1020+
* `com/example/mapper/NameMapper/NameMapper-{methodName}.sql` +
1021+
(fallback using default database)
1022+
* `com/example/mapper/BaseMapper/BaseMapper-{methodName}-{databaseId}.sql` +
1023+
(fallback using declaring class of mapper method)
1024+
* `com/example/mapper/BaseMapper/BaseMapper-{methodName}.sql` +
1025+
(fallback using declaring class of mapper method and default database)
1026+
1027+
If you want to customize the template file path format,
1028+
please call static setter methods of the `TemplateFilePathProvider` **before initialize the MyBatis module**.
1029+
1030+
[NOTE]
1031+
====
1032+
If you applied an user defined `ThymeleafLanguageDriverConfig` for `ThymeleafLanguageDriver`,
1033+
please apply same instance to the `TemplateFilePathProvider` using the `setLanguageDriverConfig` method.
1034+
====
1035+
9661036

9671037
== Cautions for usage
9681038

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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.support;
17+
18+
import java.io.IOException;
19+
import java.lang.reflect.Method;
20+
import java.util.Optional;
21+
22+
import org.apache.ibatis.builder.annotation.ProviderContext;
23+
import org.apache.ibatis.io.Resources;
24+
import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver;
25+
import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig;
26+
27+
/**
28+
* The SQL provider class that return the SQL template file path. <br>
29+
* <b>IMPORTANT: This class required to use with mybatis 3.5.1+</b> and need to use with SQL provider annotation (such
30+
* as {@link org.apache.ibatis.annotations.SelectProvider} as follow: <br>
31+
* <br>
32+
*
33+
* <pre>
34+
* package com.example.mapper;
35+
*
36+
* public interface BaseMapper&lt;T&gt; {
37+
*
38+
* &#64;Options(useGeneratedKeys = true, keyProperty = "id")
39+
* &#64;InsertProvider(type = TemplateFilePathProvider.class)
40+
* void insert(T entity);
41+
*
42+
* &#64;UpdateProvider(type = TemplateFilePathProvider.class)
43+
* void update(T entity);
44+
*
45+
* &#64;DeleteProvider(type = TemplateFilePathProvider.class)
46+
* void delete(T entity);
47+
*
48+
* &#64;SelectProvider(type = TemplateFilePathProvider.class)
49+
* T findById(Integer id);
50+
*
51+
* }
52+
* </pre>
53+
*
54+
* <pre>
55+
* package com.example.mapper;
56+
*
57+
* public interface NameMapper extends BaseMapper {
58+
*
59+
* &#64;SelectProvider(type = TemplateFilePathProvider.class)
60+
* List&lt;Name&gt; findByConditions(NameConditions conditions);
61+
*
62+
* }
63+
* </pre>
64+
*
65+
* @author Kazuki Shimizu
66+
* @version 1.0.1
67+
*/
68+
public class TemplateFilePathProvider {
69+
70+
private static final PathGenerator DEFAULT_PATH_GENERATOR = TemplateFilePathProvider::generateTemplatePath;
71+
private static final ThymeleafLanguageDriverConfig DEFAULT_LANGUAGE_DRIVER_CONFIG = ThymeleafLanguageDriverConfig
72+
.newInstance();
73+
74+
private static String prefix = "";
75+
private static boolean includesPackagePath = true;
76+
private static boolean separateDirectoryPerMapper = true;
77+
private static boolean includesMapperNameWhenSeparateDirectory = true;
78+
private static PathGenerator pathGenerator = DEFAULT_PATH_GENERATOR;
79+
private static ThymeleafLanguageDriverConfig languageDriverConfig = DEFAULT_LANGUAGE_DRIVER_CONFIG;
80+
81+
/**
82+
* Set a prefix for adding to template file path.
83+
* <p>
84+
* Default is {@code ""}.
85+
* </p>
86+
*
87+
* @param prefix
88+
* a prefix for adding to template file path
89+
*/
90+
public static void setPrefix(String prefix) {
91+
TemplateFilePathProvider.prefix = Optional.ofNullable(prefix).orElse("");
92+
}
93+
94+
/**
95+
* Set whether includes package path part.
96+
* <p>
97+
* Default is {@code true}.
98+
* </p>
99+
*
100+
* @param includesPackagePath
101+
* If want to includes, set {@code true}
102+
*/
103+
public static void setIncludesPackagePath(boolean includesPackagePath) {
104+
TemplateFilePathProvider.includesPackagePath = includesPackagePath;
105+
}
106+
107+
/**
108+
* Set whether separate directory per mapper.
109+
* <p>
110+
* Default is {@code true}.
111+
* </p>
112+
*
113+
* @param separateDirectoryPerMapper
114+
* If want to separate directory, set {@code true}
115+
*/
116+
public static void setSeparateDirectoryPerMapper(boolean separateDirectoryPerMapper) {
117+
TemplateFilePathProvider.separateDirectoryPerMapper = separateDirectoryPerMapper;
118+
}
119+
120+
/**
121+
* Set whether includes mapper name into file name when separate directory per mapper.
122+
* <p>
123+
* Default is {@code true}.
124+
* </p>
125+
*
126+
* @param includesMapperNameWhenSeparateDirectory
127+
* If want to includes, set {@code true}
128+
*/
129+
public static void setIncludesMapperNameWhenSeparateDirectory(boolean includesMapperNameWhenSeparateDirectory) {
130+
TemplateFilePathProvider.includesMapperNameWhenSeparateDirectory = includesMapperNameWhenSeparateDirectory;
131+
}
132+
133+
/**
134+
* Set custom implementation for {@link PathGenerator}.
135+
*
136+
* @param generator
137+
* a instance for generating a template file path
138+
*/
139+
public static void setCustomTemplateFilePathGenerator(PathGenerator generator) {
140+
TemplateFilePathProvider.pathGenerator = Optional.ofNullable(generator).orElse(DEFAULT_PATH_GENERATOR);
141+
}
142+
143+
/**
144+
* Set a configuration instance for {@link ThymeleafLanguageDriver}.
145+
* <p>
146+
* By default, {@link ThymeleafLanguageDriverConfig#newInstance()} will used.
147+
* </p>
148+
* <p>
149+
* If you applied an user define {@link ThymeleafLanguageDriverConfig} for {@link ThymeleafLanguageDriver}, please
150+
* same instance to the this class.
151+
* </p>
152+
*
153+
* @param languageDriverConfig
154+
* A user defined {@link ThymeleafLanguageDriverConfig}
155+
*/
156+
public static void setLanguageDriverConfig(ThymeleafLanguageDriverConfig languageDriverConfig) {
157+
TemplateFilePathProvider.languageDriverConfig = Optional.ofNullable(languageDriverConfig)
158+
.orElse(DEFAULT_LANGUAGE_DRIVER_CONFIG);
159+
}
160+
161+
/**
162+
* Provide an SQL scripting string(template file path).
163+
*
164+
* <br>
165+
* By default implementation, a template file path resolve following format and priority order. If does not match all,
166+
* it throw an exception that indicate not found a template file.
167+
* <ul>
168+
* <li>com/example/mapper/NameMapper/NameMapper-{methodName}-{databaseId}.sql</li>
169+
* <li>com/example/mapper/NameMapper/NameMapper-{methodName}.sql (fallback using default database)</li>
170+
* <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}-{databaseId}.sql (fallback using declaring class of
171+
* method)</li>
172+
* <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}.sql (fallback using declaring class of method and default
173+
* database)</li>
174+
* </ul>
175+
* <br>
176+
* If you want to customize path format, please call the following methods on application initialize phase.
177+
* <ul>
178+
* <li>{@link #setPrefix(String)}</li>
179+
* <li>{@link #setIncludesPackagePath(boolean)}</li>
180+
* <li>{@link #setSeparateDirectoryPerMapper(boolean)}</li>
181+
* <li>{@link #setIncludesMapperNameWhenSeparateDirectory(boolean)}</li>
182+
* <li>{@link #setLanguageDriverConfig(ThymeleafLanguageDriverConfig)}</li>
183+
* <li>{@link #setCustomTemplateFilePathGenerator(PathGenerator)}</li>
184+
* </ul>
185+
*
186+
* @param context
187+
* a context of SQL provider
188+
* @return an SQL scripting string(template file path)
189+
*/
190+
public static String provideSql(ProviderContext context) {
191+
return providePath(context.getMapperType(), context.getMapperMethod(), context.getDatabaseId());
192+
}
193+
194+
static String providePath(Class<?> mapperType, Method mapperMethod, String databaseId) {
195+
boolean fallbackDeclaringClass = mapperType != mapperMethod.getDeclaringClass();
196+
boolean fallbackDatabase = databaseId != null;
197+
String path = pathGenerator.generatePath(mapperType, mapperMethod, databaseId);
198+
if (exists(path)) {
199+
return path;
200+
}
201+
if (fallbackDatabase) {
202+
path = pathGenerator.generatePath(mapperType, mapperMethod, null);
203+
if (exists(path)) {
204+
return path;
205+
}
206+
}
207+
if (fallbackDeclaringClass) {
208+
path = pathGenerator.generatePath(mapperMethod.getDeclaringClass(), mapperMethod, databaseId);
209+
if (exists(path)) {
210+
return path;
211+
}
212+
}
213+
if (fallbackDatabase) {
214+
path = pathGenerator.generatePath(mapperMethod.getDeclaringClass(), mapperMethod, null);
215+
if (exists(path)) {
216+
return path;
217+
}
218+
}
219+
throw new IllegalStateException("The SQL template file not found. mapperType:[" + mapperType + "] mapperMethod:["
220+
+ mapperMethod + "] databaseId:[" + databaseId + "]");
221+
}
222+
223+
private static String generateTemplatePath(Class<?> type, Method method, String databaseId) {
224+
Package pkg = type.getPackage();
225+
String packageName = pkg == null ? "" : pkg.getName();
226+
String className = type.getName().substring(packageName.length() + (packageName.length() == 0 ? 0 : 1));
227+
228+
StringBuilder path = new StringBuilder();
229+
if (!prefix.isEmpty()) {
230+
path.append(prefix);
231+
}
232+
if (includesPackagePath && !packageName.isEmpty()) {
233+
path.append(packageName.replace('.', '/')).append('/');
234+
}
235+
path.append(className);
236+
if (separateDirectoryPerMapper) {
237+
path.append('/');
238+
if (includesMapperNameWhenSeparateDirectory) {
239+
path.append(className).append('-');
240+
}
241+
} else {
242+
path.append('-');
243+
}
244+
path.append(method.getName());
245+
if (databaseId != null) {
246+
path.append('-').append(databaseId);
247+
}
248+
path.append(".sql");
249+
return path.toString();
250+
}
251+
252+
private static boolean exists(String path) {
253+
String actualPath;
254+
if (languageDriverConfig.getTemplateFile().getBaseDir().isEmpty()) {
255+
actualPath = path;
256+
} else {
257+
actualPath = languageDriverConfig.getTemplateFile().getBaseDir().endsWith("/")
258+
? languageDriverConfig.getTemplateFile().getBaseDir() + path
259+
: languageDriverConfig.getTemplateFile().getBaseDir() + "/" + path;
260+
}
261+
try {
262+
return Resources.getResourceAsFile(actualPath).exists();
263+
} catch (IOException e) {
264+
return false;
265+
}
266+
}
267+
268+
/**
269+
* The interface that implements a function for generating template file path.
270+
*/
271+
@FunctionalInterface
272+
public interface PathGenerator {
273+
274+
/**
275+
* Generate a template file path.
276+
*
277+
* @param type
278+
* mapper interface type that specified provider (or declaring interface type of mapper method)
279+
* @param method
280+
* a mapper method that specified provider
281+
* @param databaseId
282+
* a database id that provided from {@link org.apache.ibatis.mapping.DatabaseIdProvider}
283+
* @return a template file path
284+
*/
285+
String generatePath(Class<?> type, Method method, String databaseId);
286+
287+
}
288+
289+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy