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

mybatis / freemarker-scripting / #339

01 Dec 2023 06:16PM CUT coverage: 93.561%. Remained the same
#339

Pull #175

github

web-flow
Update dependency org.mybatis:mybatis-parent to v41
Pull Request #175: Update dependency org.mybatis:mybatis-parent to v41

78 of 86 branches covered (0.0%)

247 of 264 relevant lines covered (93.56%)

0.94 hits per line

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

100.0
/src/main/java/org/mybatis/scripting/freemarker/support/TemplateFilePathProvider.java
1
/*
2
 *    Copyright 2015-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.freemarker.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.freemarker.FreeMarkerLanguageDriver;
27
import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig;
28
import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig.TemplateFileConfig.PathProviderConfig;
29

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

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

79
  private static PathGenerator pathGenerator = DEFAULT_PATH_GENERATOR;
1✔
80
  private static FreeMarkerLanguageDriverConfig languageDriverConfig = DEFAULT_LANGUAGE_DRIVER_CONFIG;
1✔
81

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

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

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

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

114
  /**
115
   * Provide an SQL scripting string(template file path).
116
   * <p>
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}.ftl</li>
121
   * <li>com/example/mapper/NameMapper/NameMapper-{methodName}.ftl (fallback using default database)</li>
122
   * <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}-{databaseId}.ftl (fallback using declaring class of
123
   * method)</li>
124
   * <li>com/example/mapper/BaseMapper/BaseMapper-{methodName}.ftl (fallback using declaring class of method and default
125
   * database)</li>
126
   * </ul>
127
   *
128
   * @param context
129
   *          a context of SQL provider
130
   *
131
   * @return an SQL scripting string(template file path)
132
   */
133
  @SuppressWarnings("unused")
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(".ftl");
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