View Javadoc
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;
17  
18  import java.io.Reader;
19  import java.lang.reflect.InvocationTargetException;
20  import java.nio.charset.StandardCharsets;
21  import java.sql.Connection;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.LinkedHashSet;
25  import java.util.List;
26  import java.util.Properties;
27  
28  import org.apache.ibatis.exceptions.PersistenceException;
29  import org.apache.ibatis.io.Resources;
30  import org.apache.ibatis.jdbc.ScriptRunner;
31  import org.apache.ibatis.mapping.Environment;
32  import org.apache.ibatis.scripting.ScriptingException;
33  import org.apache.ibatis.session.Configuration;
34  import org.apache.ibatis.session.SqlSession;
35  import org.apache.ibatis.session.SqlSessionFactory;
36  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
37  import org.apache.ibatis.transaction.TransactionFactory;
38  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
39  import org.hsqldb.jdbc.JDBCDataSource;
40  import org.junit.jupiter.api.AfterEach;
41  import org.junit.jupiter.api.Assertions;
42  import org.junit.jupiter.api.BeforeAll;
43  import org.junit.jupiter.api.BeforeEach;
44  import org.junit.jupiter.api.Test;
45  import org.mybatis.scripting.thymeleaf.expression.Likes;
46  import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name;
47  import org.mybatis.scripting.thymeleaf.integrationtest.mapper.NameMapper;
48  import org.mybatis.scripting.thymeleaf.integrationtest.mapper.NameParam;
49  import org.mybatis.scripting.thymeleaf.support.TemplateFilePathProvider;
50  import org.thymeleaf.TemplateEngine;
51  import org.thymeleaf.templatemode.TemplateMode;
52  import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
53  import org.thymeleaf.templateresolver.StringTemplateResolver;
54  
55  class ThymeleafLanguageDriverTest {
56  
57    private static SqlSessionFactory sqlSessionFactory;
58    private String currentConfigFile;
59    private String currentConfigEncoding;
60  
61    @BeforeAll
62    static void setUp() throws Exception {
63      Class.forName("org.hsqldb.jdbcDriver");
64      JDBCDataSource dataSource = new JDBCDataSource();
65      dataSource.setUrl("jdbc:hsqldb:mem:db1");
66      dataSource.setUser("sa");
67      dataSource.setPassword("");
68  
69      try (Connection conn = dataSource.getConnection()) {
70        try (Reader reader = Resources.getResourceAsReader("create-db.sql")) {
71          ScriptRunner runner = new ScriptRunner(conn);
72          runner.setLogWriter(null);
73          runner.setErrorLogWriter(null);
74          runner.runScript(reader);
75          conn.commit();
76        }
77      }
78  
79      TransactionFactory transactionFactory = new JdbcTransactionFactory();
80      Environment environment = new Environment("development", transactionFactory, dataSource);
81  
82      Configuration configuration = new Configuration(environment);
83      configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(new TemplateEngine()));
84      configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
85  
86      configuration.addMapper(NameMapper.class);
87      sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
88    }
89  
90    @BeforeEach
91    @AfterEach
92    void cleanup() {
93      TemplateFilePathProvider.setLanguageDriverConfig(null);
94    }
95  
96    @BeforeEach
97    void saveCurrentConfig() {
98      currentConfigFile = System.getProperty("mybatis-thymeleaf.config");
99      currentConfigEncoding = System.getProperty("mybatis-thymeleaf.config.encoding");
100   }
101 
102   @AfterEach
103   void restoreConfig() {
104     if (currentConfigFile == null) {
105       System.clearProperty("mybatis-thymeleaf.config.file");
106     } else {
107       System.setProperty("mybatis-thymeleaf.config.file", currentConfigFile);
108     }
109     if (currentConfigEncoding == null) {
110       System.clearProperty("mybatis-thymeleaf.config.encoding");
111     } else {
112       System.setProperty("mybatis-thymeleaf.config.encoding", currentConfigEncoding);
113     }
114   }
115 
116   @Test
117   void testDefaultConfig() {
118     DefaultTemplateEngineCustomizer.templateEngine = null;
119     Configuration configuration = new Configuration();
120     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
121     new SqlSessionFactoryBuilder().build(configuration);
122 
123     TemplateEngine templateEngine = DefaultTemplateEngineCustomizer.templateEngine;
124     ClassLoaderTemplateResolver classLoaderTemplateResolver = TemplateEngineCustomizer
125         .extractTemplateResolver(templateEngine, ClassLoaderTemplateResolver.class)
126         .orElseGet(() -> Assertions.fail("Cannot a ClassLoaderTemplateResolver instance."));
127 
128     Assertions.assertEquals(TemplateMode.CSS, classLoaderTemplateResolver.getTemplateMode());
129     Assertions.assertTrue(classLoaderTemplateResolver.isCacheable());
130     Assertions.assertNull(classLoaderTemplateResolver.getCacheTTLMs());
131     Assertions.assertEquals("UTF-8", classLoaderTemplateResolver.getCharacterEncoding());
132     Assertions.assertEquals("", classLoaderTemplateResolver.getPrefix());
133     Assertions.assertEquals(new LinkedHashSet<>(Collections.singleton("*.sql")),
134         classLoaderTemplateResolver.getResolvablePatterns());
135 
136     StringTemplateResolver stringTemplateResolver = TemplateEngineCustomizer
137         .extractTemplateResolver(templateEngine, StringTemplateResolver.class)
138         .orElseGet(() -> Assertions.fail("Cannot a StringTemplateResolver instance."));
139     Assertions.assertEquals(TemplateMode.CSS, stringTemplateResolver.getTemplateMode());
140     Assertions.assertFalse(stringTemplateResolver.isCacheable());
141     Assertions.assertNull(stringTemplateResolver.getCacheTTLMs());
142 
143     templateEngine.getDialects().stream().filter(MyBatisDialect.class::isInstance).findFirst()
144         .map(MyBatisDialect.class::cast).ifPresent(v -> {
145           Assertions.assertEquals("mb", v.getPrefix());
146           Likes expression = (Likes) v.getExpressionObjectFactory().buildObject(null, null);
147           Assertions.assertEquals("ESCAPE '\\'", expression.escapeClause());
148         });
149   }
150 
151   @Test
152   void testCustomWithCustomConfigFileUsingSystemProperty() {
153     System.setProperty("mybatis-thymeleaf.config.file", "mybatis-thymeleaf-custom.properties");
154     Configuration configuration = new Configuration();
155     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
156 
157     new SqlSessionFactoryBuilder().build(configuration);
158 
159     TemplateEngine templateEngine = CustomTemplateEngineCustomizer.templateEngine;
160     ClassLoaderTemplateResolver classLoaderTemplateResolver = TemplateEngineCustomizer
161         .extractTemplateResolver(templateEngine, ClassLoaderTemplateResolver.class)
162         .orElseGet(() -> Assertions.fail("Cannot a ClassLoaderTemplateResolver instance."));
163 
164     Assertions.assertEquals(TemplateMode.TEXT, classLoaderTemplateResolver.getTemplateMode());
165     Assertions.assertFalse(classLoaderTemplateResolver.isCacheable());
166     Assertions.assertEquals(Long.valueOf(30000), classLoaderTemplateResolver.getCacheTTLMs());
167     Assertions.assertEquals("ISO-8859-1", classLoaderTemplateResolver.getCharacterEncoding());
168     Assertions.assertEquals("templates/", classLoaderTemplateResolver.getPrefix());
169     Assertions.assertEquals(new LinkedHashSet<>(Arrays.asList("*.sql", "*.sql.template")),
170         classLoaderTemplateResolver.getResolvablePatterns());
171 
172     StringTemplateResolver stringTemplateResolver = TemplateEngineCustomizer
173         .extractTemplateResolver(templateEngine, StringTemplateResolver.class)
174         .orElseGet(() -> Assertions.fail("Cannot a StringTemplateResolver instance."));
175     Assertions.assertEquals(TemplateMode.TEXT, stringTemplateResolver.getTemplateMode());
176     Assertions.assertFalse(stringTemplateResolver.isCacheable());
177 
178     templateEngine.getDialects().stream().filter(MyBatisDialect.class::isInstance).findFirst()
179         .map(MyBatisDialect.class::cast).ifPresent(v -> {
180           Assertions.assertEquals("mybatis", v.getPrefix());
181           Likes expression = (Likes) v.getExpressionObjectFactory().buildObject(null, null);
182           Assertions.assertEquals("escape '~'", expression.escapeClause());
183           Assertions.assertEquals("a~%~_~~b", expression.escapeWildcard("a%_~b"));
184         });
185   }
186 
187   @Test
188   void testCustomWithCustomConfigFileUsingMethodArgument() {
189     ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig = ThymeleafLanguageDriverConfig
190         .newInstance("mybatis-thymeleaf-custom.properties");
191     Configuration configuration = new Configuration();
192     configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(thymeleafLanguageDriverConfig));
193     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
194 
195     new SqlSessionFactoryBuilder().build(configuration);
196 
197     TemplateEngine templateEngine = CustomTemplateEngineCustomizer.templateEngine;
198     ClassLoaderTemplateResolver classLoaderTemplateResolver = TemplateEngineCustomizer
199         .extractTemplateResolver(templateEngine, ClassLoaderTemplateResolver.class)
200         .orElseGet(() -> Assertions.fail("Cannot a ClassLoaderTemplateResolver instance."));
201 
202     Assertions.assertEquals(TemplateMode.TEXT, classLoaderTemplateResolver.getTemplateMode());
203     Assertions.assertFalse(classLoaderTemplateResolver.isCacheable());
204     Assertions.assertEquals(Long.valueOf(30000), classLoaderTemplateResolver.getCacheTTLMs());
205     Assertions.assertEquals("ISO-8859-1", classLoaderTemplateResolver.getCharacterEncoding());
206     Assertions.assertEquals("templates/", classLoaderTemplateResolver.getPrefix());
207     Assertions.assertEquals(new LinkedHashSet<>(Arrays.asList("*.sql", "*.sql.template")),
208         classLoaderTemplateResolver.getResolvablePatterns());
209 
210     StringTemplateResolver stringTemplateResolver = TemplateEngineCustomizer
211         .extractTemplateResolver(templateEngine, StringTemplateResolver.class)
212         .orElseGet(() -> Assertions.fail("Cannot a StringTemplateResolver instance."));
213     Assertions.assertEquals(TemplateMode.TEXT, stringTemplateResolver.getTemplateMode());
214     Assertions.assertFalse(stringTemplateResolver.isCacheable());
215 
216     templateEngine.getDialects().stream().filter(MyBatisDialect.class::isInstance).findFirst()
217         .map(MyBatisDialect.class::cast).ifPresent(v -> {
218           Assertions.assertEquals("mybatis", v.getPrefix());
219           Likes expression = (Likes) v.getExpressionObjectFactory().buildObject(null, null);
220           Assertions.assertEquals("escape '~'", expression.escapeClause());
221           Assertions.assertEquals("a~%~_~~b", expression.escapeWildcard("a%_~b"));
222         });
223 
224     Assertions.assertEquals("sqls/", thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().getPrefix());
225     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesPackagePath());
226     Assertions
227         .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper());
228     Assertions.assertFalse(
229         thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory());
230     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled());
231 
232   }
233 
234   @Test
235   void testCustomWithCustomizerFunction() {
236     System.setProperty("mybatis-thymeleaf.config.file", "mybatis-thymeleaf-empty.properties");
237     ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig = ThymeleafLanguageDriverConfig.newInstance(c -> {
238       c.setUse2way(false);
239       c.setCustomizerInstance(new CustomTemplateEngineCustomizer());
240       c.getTemplateFile().setCacheEnabled(false);
241       c.getTemplateFile().setCacheTtl(30000L);
242       c.getTemplateFile().setEncoding(StandardCharsets.ISO_8859_1);
243       c.getTemplateFile().setBaseDir("templates/");
244       c.getTemplateFile().setPatterns("*.sql", "*.sql.template");
245       c.getTemplateFile().getPathProvider().setPrefix("sqls/");
246       c.getTemplateFile().getPathProvider().setIncludesPackagePath(false);
247       c.getTemplateFile().getPathProvider().setSeparateDirectoryPerMapper(false);
248       c.getTemplateFile().getPathProvider().setIncludesMapperNameWhenSeparateDirectory(false);
249       c.getTemplateFile().getPathProvider().setCacheEnabled(false);
250       c.getDialect().setPrefix("mbs");
251       c.getDialect().setLikeEscapeChar('~');
252       c.getDialect().setLikeEscapeClauseFormat("escape '%s'");
253       c.getDialect().setLikeAdditionalEscapeTargetChars('%', '_');
254     });
255     Configuration configuration = new Configuration();
256     configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(thymeleafLanguageDriverConfig));
257     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
258 
259     new SqlSessionFactoryBuilder().build(configuration);
260 
261     TemplateEngine templateEngine = CustomTemplateEngineCustomizer.templateEngine;
262     ClassLoaderTemplateResolver classLoaderTemplateResolver = TemplateEngineCustomizer
263         .extractTemplateResolver(templateEngine, ClassLoaderTemplateResolver.class)
264         .orElseGet(() -> Assertions.fail("Cannot a ClassLoaderTemplateResolver instance."));
265 
266     Assertions.assertEquals(TemplateMode.TEXT, classLoaderTemplateResolver.getTemplateMode());
267     Assertions.assertFalse(classLoaderTemplateResolver.isCacheable());
268     Assertions.assertEquals(Long.valueOf(30000), classLoaderTemplateResolver.getCacheTTLMs());
269     Assertions.assertEquals("ISO-8859-1", classLoaderTemplateResolver.getCharacterEncoding());
270     Assertions.assertEquals("templates/", classLoaderTemplateResolver.getPrefix());
271     Assertions.assertEquals(new LinkedHashSet<>(Arrays.asList("*.sql", "*.sql.template")),
272         classLoaderTemplateResolver.getResolvablePatterns());
273 
274     StringTemplateResolver stringTemplateResolver = TemplateEngineCustomizer
275         .extractTemplateResolver(templateEngine, StringTemplateResolver.class)
276         .orElseGet(() -> Assertions.fail("Cannot a StringTemplateResolver instance."));
277     Assertions.assertEquals(TemplateMode.TEXT, stringTemplateResolver.getTemplateMode());
278     Assertions.assertFalse(stringTemplateResolver.isCacheable());
279 
280     templateEngine.getDialects().stream().filter(MyBatisDialect.class::isInstance).findFirst()
281         .map(MyBatisDialect.class::cast).ifPresent(v -> {
282           Assertions.assertEquals("mbs", v.getPrefix());
283           Likes expression = (Likes) v.getExpressionObjectFactory().buildObject(null, null);
284           Assertions.assertEquals("escape '~'", expression.escapeClause());
285           Assertions.assertEquals("a~%~_~~b", expression.escapeWildcard("a%_~b"));
286         });
287 
288     Assertions.assertEquals("sqls/", thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().getPrefix());
289     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesPackagePath());
290     Assertions
291         .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper());
292     Assertions.assertFalse(
293         thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory());
294     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled());
295   }
296 
297   @Test
298   void testCustomWithBuilderUsingCustomProperties() {
299     System.setProperty("mybatis-thymeleaf.config.file", "mybatis-thymeleaf-empty.properties");
300     Configuration configuration = new Configuration();
301     Properties customProperties = new Properties();
302     customProperties.setProperty("use2way", "false");
303     customProperties.setProperty("customizer", "org.mybatis.scripting.thymeleaf.CustomTemplateEngineCustomizer");
304     customProperties.setProperty("template-file.cache-enabled", "false");
305     customProperties.setProperty("template-file.cache-ttl", "30000");
306     customProperties.setProperty("template-file.encoding", "ISO-8859-1");
307     customProperties.setProperty("template-file.base-dir", "templates/");
308     customProperties.setProperty("template-file.patterns", "*.sql, *.sql.template");
309     customProperties.setProperty("template-file.path-provider.prefix", "sqls/");
310     customProperties.setProperty("template-file.path-provider.includes-package-path", "false");
311     customProperties.setProperty("template-file.path-provider.separate-directory-per-mapper", "false");
312     customProperties.setProperty("template-file.path-provider.includes-mapper-name-when-separate-directory", "false");
313     customProperties.setProperty("template-file.path-provider.cache-enabled", "false");
314     customProperties.setProperty("dialect.prefix", "mbs");
315     customProperties.setProperty("dialect.like-escape-char", "~");
316     customProperties.setProperty("dialect.like-escape-clause-format", "escape '%s'");
317     customProperties.setProperty("dialect.like-additional-escape-target-chars", "%,_");
318 
319     ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig = ThymeleafLanguageDriverConfig
320         .newInstance(customProperties);
321 
322     configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver(thymeleafLanguageDriverConfig));
323     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
324 
325     new SqlSessionFactoryBuilder().build(configuration);
326 
327     TemplateEngine templateEngine = CustomTemplateEngineCustomizer.templateEngine;
328     ClassLoaderTemplateResolver classLoaderTemplateResolver = TemplateEngineCustomizer
329         .extractTemplateResolver(templateEngine, ClassLoaderTemplateResolver.class)
330         .orElseGet(() -> Assertions.fail("Cannot a ClassLoaderTemplateResolver instance."));
331 
332     Assertions.assertEquals(TemplateMode.TEXT, classLoaderTemplateResolver.getTemplateMode());
333     Assertions.assertFalse(classLoaderTemplateResolver.isCacheable());
334     Assertions.assertEquals(Long.valueOf(30000), classLoaderTemplateResolver.getCacheTTLMs());
335     Assertions.assertEquals("ISO-8859-1", classLoaderTemplateResolver.getCharacterEncoding());
336     Assertions.assertEquals("templates/", classLoaderTemplateResolver.getPrefix());
337     Assertions.assertEquals(new LinkedHashSet<>(Arrays.asList("*.sql", "*.sql.template")),
338         classLoaderTemplateResolver.getResolvablePatterns());
339 
340     StringTemplateResolver stringTemplateResolver = TemplateEngineCustomizer
341         .extractTemplateResolver(templateEngine, StringTemplateResolver.class)
342         .orElseGet(() -> Assertions.fail("Cannot a StringTemplateResolver instance."));
343     Assertions.assertEquals(TemplateMode.TEXT, stringTemplateResolver.getTemplateMode());
344     Assertions.assertFalse(stringTemplateResolver.isCacheable());
345 
346     templateEngine.getDialects().stream().filter(MyBatisDialect.class::isInstance).findFirst()
347         .map(MyBatisDialect.class::cast).ifPresent(v -> {
348           Assertions.assertEquals("mbs", v.getPrefix());
349           Likes expression = (Likes) v.getExpressionObjectFactory().buildObject(null, null);
350           Assertions.assertEquals("escape '~'", expression.escapeClause());
351           Assertions.assertEquals("a~%~_~~b", expression.escapeWildcard("a%_~b"));
352         });
353 
354     Assertions.assertEquals("sqls/", thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().getPrefix());
355     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesPackagePath());
356     Assertions
357         .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper());
358     Assertions.assertFalse(
359         thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory());
360     Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled());
361   }
362 
363   @Test
364   void testConfigFileNotFound() {
365     System.setProperty("mybatis-thymeleaf.config.file", "mybatis-thymeleaf-dummy.properties");
366     Configuration configuration = new Configuration();
367     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
368     Assertions.assertEquals(ThymeleafLanguageDriver.class, configuration.getLanguageRegistry().getDefaultDriverClass());
369   }
370 
371   @Test
372   void testConfigFileNotFoundAtMethodArgument() {
373     ThymeleafLanguageDriverConfig.newInstance("mybatis-thymeleaf-dummy.properties");
374     Configuration configuration = new Configuration();
375     configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
376     Assertions.assertEquals(ThymeleafLanguageDriver.class, configuration.getLanguageRegistry().getDefaultDriverClass());
377   }
378 
379   @Test
380   void testCustomizerNotFound() {
381     System.setProperty("mybatis-thymeleaf.config.file", "mybatis-thymeleaf-customizer-not-found.properties");
382     Configuration configuration = new Configuration();
383     try {
384       configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
385       Assertions.fail();
386     } catch (ScriptingException e) {
387       Assertions.assertEquals(
388           "Failed to load language driver for org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver", e.getMessage());
389       // Since mybatis 3.5.1, exception is wrapped by InvocationTargetException
390       Throwable cause = e.getCause() instanceof InvocationTargetException ? e.getCause().getCause() : e.getCause();
391       Assertions.assertEquals(
392           "java.lang.ClassNotFoundException: org.mybatis.scripting.thymeleaf.FooTemplateEngineCustomizer",
393           cause.getMessage());
394     }
395   }
396 
397   @Test
398   void testCustomizerNotCreation() {
399     System.setProperty("mybatis-thymeleaf.config.file",
400         "mybatis-thymeleaf-customizer-no-default-constructor.properties");
401     Configuration configuration = new Configuration();
402     try {
403       configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class);
404       Assertions.fail();
405     } catch (ScriptingException e) {
406       Assertions.assertEquals(
407           "Failed to load language driver for org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver", e.getMessage());
408       // Since mybatis 3.5.1, exception is wrapped by InvocationTargetException
409       Throwable cause = e.getCause() instanceof InvocationTargetException
410           ? e.getCause().getCause().getCause().getCause() : e.getCause();
411       Assertions.assertEquals(
412           "Cannot create an instance for class: class org.mybatis.scripting.thymeleaf.NoDefaultConstructorTemplateEngineCustomizer",
413           cause.getMessage());
414     }
415   }
416 
417   @Test
418   void testCanResolveStringTemplateByUserDefineTemplateEngine() {
419     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
420       NameMapper mapper = sqlSession.getMapper(NameMapper.class);
421       List<Name> names = mapper.getAllNames();
422       Assertions.assertEquals(7, names.size());
423     }
424   }
425 
426   @Test
427   void testCanNotResolveTemplateFileByUserDefineTemplateEngine() {
428     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
429       NameMapper mapper = sqlSession.getMapper(NameMapper.class);
430       mapper.findUsingTemplateFile(new NameParam(3));
431       Assertions.fail();
432     } catch (PersistenceException e) {
433       Assertions.assertEquals("unexpected token: SQL in statement [sql/NameMapper/findById.sql]",
434           e.getCause().getMessage());
435     }
436   }
437 
438 }