• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / thymeleaf-scripting / 660

03 Jan 2025 09:56PM CUT coverage: 97.255%. Remained the same
660

Pull #299

github

web-flow
Merge 1267bbb9c into 123bff1f0
Pull Request #299: Update dependency org.mybatis:mybatis to v3.5.19

133 of 144 branches covered (92.36%)

Branch coverage included in aggregate %.

611 of 621 relevant lines covered (98.39%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.99
/src/main/java/org/mybatis/scripting/thymeleaf/support/TemplateFilePathProvider.java
1
/*
2
 *    Copyright 2018-2022 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
 *       https://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
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.concurrent.ConcurrentMap;
23

24
import org.apache.ibatis.builder.annotation.ProviderContext;
25
import org.apache.ibatis.io.Resources;
26
import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver;
27
import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig;
28
import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig.TemplateFileConfig.PathProviderConfig;
29

30
/**
31
 * The SQL provider class that return the SQL template file path. <br>
32
 * <b>IMPORTANT: This class required to use with mybatis 3.5.1+</b> and need to use with SQL provider annotation (such
33
 * as {@link org.apache.ibatis.annotations.SelectProvider} as follow: <br>
34
 * <br>
35
 *
36
 * <pre>
37
 * package com.example.mapper;
38
 *
39
 * public interface BaseMapper&lt;T&gt; {
40
 *
41
 *   &#64;Options(useGeneratedKeys = true, keyProperty = "id")
42
 *   &#64;InsertProvider(type = TemplateFilePathProvider.class)
43
 *   void insert(T entity);
44
 *
45
 *   &#64;UpdateProvider(type = TemplateFilePathProvider.class)
46
 *   void update(T entity);
47
 *
48
 *   &#64;DeleteProvider(type = TemplateFilePathProvider.class)
49
 *   void delete(T entity);
50
 *
51
 *   &#64;SelectProvider(type = TemplateFilePathProvider.class)
52
 *   T findById(Integer id);
53
 *
54
 * }
55
 * </pre>
56
 *
57
 * <pre>
58
 * package com.example.mapper;
59
 *
60
 * public interface NameMapper extends BaseMapper {
61
 *
62
 *   &#64;SelectProvider(type = TemplateFilePathProvider.class)
63
 *   List&lt;Name&gt; findByConditions(NameConditions conditions);
64
 *
65
 * }
66
 * </pre>
67
 *
68
 * @author Kazuki Shimizu
69
 *
70
 * @version 1.0.1
71
 */
72
public class TemplateFilePathProvider {
73

74
  private static final PathGenerator DEFAULT_PATH_GENERATOR = TemplateFilePathProvider::generateTemplatePath;
1✔
75
  private static final ThymeleafLanguageDriverConfig DEFAULT_LANGUAGE_DRIVER_CONFIG = ThymeleafLanguageDriverConfig
76
      .newInstance();
1✔
77

78
  private static PathGenerator pathGenerator = DEFAULT_PATH_GENERATOR;
1✔
79
  private static ThymeleafLanguageDriverConfig languageDriverConfig = DEFAULT_LANGUAGE_DRIVER_CONFIG;
1✔
80

81
  private static ConcurrentMap<ProviderContext, String> cache = new ConcurrentHashMap<>();
1✔
82

83
  private TemplateFilePathProvider() {
84
    // NOP
85
  }
86

87
  /**
88
   * Set custom implementation for {@link PathGenerator}.
89
   *
90
   * @param pathGenerator
91
   *          a instance for generating a template file path
92
   */
93
  public static void setCustomTemplateFilePathGenerator(PathGenerator pathGenerator) {
94
    TemplateFilePathProvider.pathGenerator = Optional.ofNullable(pathGenerator).orElse(DEFAULT_PATH_GENERATOR);
1✔
95
  }
1✔
96

97
  /**
98
   * Set a configuration instance for {@link ThymeleafLanguageDriver}.
99
   * <p>
100
   * By default, {@link ThymeleafLanguageDriverConfig#newInstance()} will used.
101
   * </p>
102
   * <p>
103
   * If you applied an user define {@link ThymeleafLanguageDriverConfig} for {@link ThymeleafLanguageDriver}, please
104
   * same instance to the this class.
105
   * </p>
106
   *
107
   * @param languageDriverConfig
108
   *          A user defined {@link ThymeleafLanguageDriverConfig}
109
   */
110
  public static void setLanguageDriverConfig(ThymeleafLanguageDriverConfig languageDriverConfig) {
111
    TemplateFilePathProvider.languageDriverConfig = Optional.ofNullable(languageDriverConfig)
1✔
112
        .orElse(DEFAULT_LANGUAGE_DRIVER_CONFIG);
1✔
113
  }
1✔
114

115
  /**
116
   * Provide an SQL scripting string(template file path). <br>
117
   * By default implementation, a template file path resolve following format and priority order. If does not match all,
118
   * it throw an exception that indicate not found a template file.
119
   * <ul>
120
   * <li>com/example/mapper/NameMapper/NameMapper-{methodName}-{databaseId}.sql</li>
121
   * <li>com/example/mapper/NameMapper/NameMapper-{methodName}.sql (fallback using default database)</li>
122
   * <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}-{databaseId}.sql (fallback using declaring class of
123
   * method)</li>
124
   * <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}.sql (fallback using declaring class of method and default
125
   * database)</li>
126
   * </ul>
127
   * <br>
128
   *
129
   * @param context
130
   *          a context of SQL provider
131
   *
132
   * @return an SQL scripting string(template file path)
133
   */
134
  public static String provideSql(ProviderContext context) {
135
    return languageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled()
1✔
136
        ? cache.computeIfAbsent(context, c -> providePath(c.getMapperType(), c.getMapperMethod(), c.getDatabaseId()))
1✔
137
        : providePath(context.getMapperType(), context.getMapperMethod(), context.getDatabaseId());
1✔
138
  }
139

140
  /**
141
   * Clear cache.
142
   */
143
  public static void clearCache() {
144
    cache.clear();
1✔
145
  }
1✔
146

147
  static String providePath(Class<?> mapperType, Method mapperMethod, String databaseId) {
148
    boolean fallbackDeclaringClass = mapperType != mapperMethod.getDeclaringClass();
1✔
149
    boolean fallbackDatabase = databaseId != null;
1✔
150
    String path = pathGenerator.generatePath(mapperType, mapperMethod, databaseId);
1✔
151
    if (exists(path)) {
1✔
152
      return path;
1✔
153
    }
154
    if (fallbackDatabase) {
1✔
155
      path = pathGenerator.generatePath(mapperType, mapperMethod, null);
1✔
156
      if (exists(path)) {
1✔
157
        return path;
1✔
158
      }
159
    }
160
    if (fallbackDeclaringClass) {
1✔
161
      path = pathGenerator.generatePath(mapperMethod.getDeclaringClass(), mapperMethod, databaseId);
1✔
162
      if (exists(path)) {
1✔
163
        return path;
1✔
164
      }
165
      if (fallbackDatabase) {
1✔
166
        path = pathGenerator.generatePath(mapperMethod.getDeclaringClass(), mapperMethod, null);
1✔
167
        if (exists(path)) {
1✔
168
          return path;
1✔
169
        }
170
      }
171
    }
172
    throw new IllegalStateException("The SQL template file not found. mapperType:[" + mapperType + "] mapperMethod:["
1✔
173
        + mapperMethod + "] databaseId:[" + databaseId + "]");
174
  }
175

176
  private static String generateTemplatePath(Class<?> type, Method method, String databaseId) {
177
    Package pkg = type.getPackage();
1✔
178
    String packageName = pkg == null ? "" : pkg.getName();
1!
179
    String className = type.getName().substring(packageName.length() + (packageName.isEmpty() ? 0 : 1));
1✔
180

181
    PathProviderConfig pathProviderConfig = languageDriverConfig.getTemplateFile().getPathProvider();
1✔
182
    StringBuilder path = new StringBuilder();
1✔
183
    if (!pathProviderConfig.getPrefix().isEmpty()) {
1✔
184
      path.append(pathProviderConfig.getPrefix());
1✔
185
    }
186
    if (pathProviderConfig.isIncludesPackagePath() && !packageName.isEmpty()) {
1✔
187
      path.append(packageName.replace('.', '/')).append('/');
1✔
188
    }
189
    path.append(className);
1✔
190
    if (pathProviderConfig.isSeparateDirectoryPerMapper()) {
1✔
191
      path.append('/');
1✔
192
      if (pathProviderConfig.isIncludesMapperNameWhenSeparateDirectory()) {
1✔
193
        path.append(className).append('-');
1✔
194
      }
195
    } else {
196
      path.append('-');
1✔
197
    }
198
    path.append(method.getName());
1✔
199
    if (databaseId != null) {
1✔
200
      path.append('-').append(databaseId);
1✔
201
    }
202
    path.append(".sql");
1✔
203
    return path.toString();
1✔
204
  }
205

206
  private static boolean exists(String path) {
207
    String basePath = languageDriverConfig.getTemplateFile().getBaseDir();
1✔
208
    String actualPath = basePath.isEmpty() ? path : basePath + (basePath.endsWith("/") ? "" : "/") + path;
1✔
209
    try {
210
      Resources.getResourceURL(actualPath);
1✔
211
      return true;
1✔
212
    } catch (IOException e) {
1✔
213
      return false;
1✔
214
    }
215
  }
216

217
  /**
218
   * The interface that implements a function for generating template file path.
219
   */
220
  @FunctionalInterface
221
  public interface PathGenerator {
222

223
    /**
224
     * Generate a template file path.
225
     *
226
     * @param type
227
     *          mapper interface type that specified provider (or declaring interface type of mapper method)
228
     * @param method
229
     *          a mapper method that specified provider
230
     * @param databaseId
231
     *          a database id that provided from {@link org.apache.ibatis.mapping.DatabaseIdProvider}
232
     *
233
     * @return a template file path
234
     */
235
    String generatePath(Class<?> type, Method method, String databaseId);
236

237
  }
238

239
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc