From 1eb7c7fa3d6ab50e370e741002bf1fc8b4082c75 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 15 Apr 2019 14:41:38 +0900 Subject: [PATCH] Support cache feature on TemplateFilePathProvider See gh-10 --- src/main/asciidoc/user-guide.adoc | 7 +- .../ThymeleafLanguageDriverConfig.java | 42 +++++- .../support/TemplateFilePathProvider.java | 16 ++- .../ThymeleafLanguageDriverTest.java | 5 + ...lateFilePathProviderMapperNoCacheTest.java | 130 ++++++++++++++++++ .../TemplateFilePathProviderMapperTest.java | 8 ++ .../mybatis-thymeleaf-custom.properties | 1 + 7 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperNoCacheTest.java diff --git a/src/main/asciidoc/user-guide.adoc b/src/main/asciidoc/user-guide.adoc index 9aa9423..26d02b9 100644 --- a/src/main/asciidoc/user-guide.adoc +++ b/src/main/asciidoc/user-guide.adoc @@ -1124,7 +1124,7 @@ The mybatis-thymeleaf provides following properties for customizing configuratio |`String[]` |`"*.sql"` -4+|*Template file path provider configuration(TemplateFilePathProvider)* +4+|*Template file path provider configuration(for TemplateFilePathProvider)* |`template-file.path-provider.prefix` |The prefix for adding to template file path @@ -1146,6 +1146,11 @@ The mybatis-thymeleaf provides following properties for customizing configuratio |`Boolean` |`true` (includes mapper name) +|`template-file.path-provider.cache-enabled` +|Whether cache a resolved template file path +|`Boolean` +|`true` (cache a resolved template file path) + 4+|*Dialect configuration* |`dialect.prefix` diff --git a/src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverConfig.java b/src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverConfig.java index a2c3ade..2d98718 100644 --- a/src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverConfig.java +++ b/src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverConfig.java @@ -331,6 +331,11 @@ public static class PathProviderConfig { */ private boolean includesMapperNameWhenSeparateDirectory = true; + /** + * Whether cache a resolved template file path. + */ + private boolean cacheEnabled = true; + /** * Get a prefix for adding to template file path. *

@@ -403,7 +408,7 @@ public void setSeparateDirectoryPerMapper(boolean separateDirectoryPerMapper) { * Default is {@code true}. *

* - * @return If includes mapper name, set {@code true} + * @return If includes mapper name, return {@code true} */ public boolean isIncludesMapperNameWhenSeparateDirectory() { return includesMapperNameWhenSeparateDirectory; @@ -422,6 +427,28 @@ public void setIncludesMapperNameWhenSeparateDirectory(boolean includesMapperNam this.includesMapperNameWhenSeparateDirectory = includesMapperNameWhenSeparateDirectory; } + /** + * Get whether cache a resolved template file path. + *

+ * Default is {@code true}. + *

+ * + * @return If cache a resolved template file path, return {@code true} + */ + public boolean isCacheEnabled() { + return cacheEnabled; + } + + /** + * Set whether cache a resolved template file path. + * + * @param cacheEnabled + * If want to cache, set {@code true} + */ + public void setCacheEnabled(boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + } + } } @@ -600,7 +627,6 @@ public void setLikeAdditionalEscapeTargetChars(Character... likeAdditionalEscape * {@code "*.sql"} * * - * * Template file path provider configuration(TemplateFilePathProvider) * * @@ -609,24 +635,26 @@ public void setLikeAdditionalEscapeTargetChars(Character... likeAdditionalEscape * {@code ""} * * - * * template-file.path-provider.includes-package-path * Whether includes package path part * {@code true} * * - * - * template-file.patterns + * template-file.path-provider.separate-directory-per-mapper * Whether separate directory per mapper * {@code true} * * - * - * template-file.patterns + * template-file.path-provider.includes-mapper-name-when-separate-directory * Whether includes mapper name into file name when separate directory per mapper * {@code true} * * + * template-file.path-provider.cache-enabled + * Whether cache a resolved template file path + * {@code true} + * + * * Dialect configuration * * diff --git a/src/main/java/org/mybatis/scripting/thymeleaf/support/TemplateFilePathProvider.java b/src/main/java/org/mybatis/scripting/thymeleaf/support/TemplateFilePathProvider.java index 3058f40..09be7f2 100644 --- a/src/main/java/org/mybatis/scripting/thymeleaf/support/TemplateFilePathProvider.java +++ b/src/main/java/org/mybatis/scripting/thymeleaf/support/TemplateFilePathProvider.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.lang.reflect.Method; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.ibatis.builder.annotation.ProviderContext; import org.apache.ibatis.io.Resources; @@ -75,6 +77,8 @@ public class TemplateFilePathProvider { private static PathGenerator pathGenerator = DEFAULT_PATH_GENERATOR; private static ThymeleafLanguageDriverConfig languageDriverConfig = DEFAULT_LANGUAGE_DRIVER_CONFIG; + private static ConcurrentMap cache = new ConcurrentHashMap<>(); + /** * Set custom implementation for {@link PathGenerator}. * @@ -124,7 +128,16 @@ public static void setLanguageDriverConfig(ThymeleafLanguageDriverConfig languag * @return an SQL scripting string(template file path) */ public static String provideSql(ProviderContext context) { - return providePath(context.getMapperType(), context.getMapperMethod(), context.getDatabaseId()); + return languageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled() + ? cache.computeIfAbsent(context, c -> providePath(c.getMapperType(), c.getMapperMethod(), c.getDatabaseId())) + : providePath(context.getMapperType(), context.getMapperMethod(), context.getDatabaseId()); + } + + /** + * Clear cache. + */ + public static void clearCache() { + cache.clear(); } static String providePath(Class mapperType, Method mapperMethod, String databaseId) { @@ -183,7 +196,6 @@ private static String generateTemplatePath(Class type, Method method, String path.append('-').append(databaseId); } path.append(".sql"); - System.out.println(path); return path.toString(); } diff --git a/src/test/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverTest.java b/src/test/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverTest.java index 4403184..7b88b9a 100644 --- a/src/test/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverTest.java +++ b/src/test/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriverTest.java @@ -227,6 +227,7 @@ void testCustomWithCustomConfigFileUsingMethodArgument() { .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper()); Assertions.assertFalse( thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory()); + Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled()); } @@ -245,6 +246,7 @@ void testCustomWithCustomizerFunction() { c.getTemplateFile().getPathProvider().setIncludesPackagePath(false); c.getTemplateFile().getPathProvider().setSeparateDirectoryPerMapper(false); c.getTemplateFile().getPathProvider().setIncludesMapperNameWhenSeparateDirectory(false); + c.getTemplateFile().getPathProvider().setCacheEnabled(false); c.getDialect().setPrefix("mbs"); c.getDialect().setLikeEscapeChar('~'); c.getDialect().setLikeEscapeClauseFormat("escape '%s'"); @@ -289,6 +291,7 @@ void testCustomWithCustomizerFunction() { .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper()); Assertions.assertFalse( thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory()); + Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled()); } @Test @@ -307,6 +310,7 @@ void testCustomWithBuilderUsingCustomProperties() { customProperties.setProperty("template-file.path-provider.includes-package-path", "false"); customProperties.setProperty("template-file.path-provider.separate-directory-per-mapper", "false"); customProperties.setProperty("template-file.path-provider.includes-mapper-name-when-separate-directory", "false"); + customProperties.setProperty("template-file.path-provider.cache-enabled", "false"); customProperties.setProperty("dialect.prefix", "mbs"); customProperties.setProperty("dialect.like-escape-char", "~"); customProperties.setProperty("dialect.like-escape-clause-format", "escape '%s'"); @@ -353,6 +357,7 @@ void testCustomWithBuilderUsingCustomProperties() { .assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isSeparateDirectoryPerMapper()); Assertions.assertFalse( thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isIncludesMapperNameWhenSeparateDirectory()); + Assertions.assertFalse(thymeleafLanguageDriverConfig.getTemplateFile().getPathProvider().isCacheEnabled()); } @Test diff --git a/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperNoCacheTest.java b/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperNoCacheTest.java new file mode 100644 index 0000000..64361d0 --- /dev/null +++ b/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperNoCacheTest.java @@ -0,0 +1,130 @@ +/** + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mybatis.scripting.thymeleaf.integrationtest; + +import java.io.Reader; +import java.sql.Connection; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.hsqldb.jdbc.JDBCDataSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; +import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver; +import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig; +import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; +import org.mybatis.scripting.thymeleaf.integrationtest.mapper.TemplateFilePathProviderMapper; + +@DisabledIfSystemProperty(named = "mybatis.version", matches = "3\\.4\\..*|3\\.5\\.0") +class TemplateFilePathProviderMapperNoCacheTest { + private static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + Class.forName("org.hsqldb.jdbcDriver"); + JDBCDataSource dataSource = new JDBCDataSource(); + dataSource.setUrl("jdbc:hsqldb:mem:db1"); + dataSource.setUser("sa"); + dataSource.setPassword(""); + + try (Connection conn = dataSource.getConnection()) { + try (Reader reader = Resources.getResourceAsReader("create-db.sql")) { + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + runner.setErrorLogWriter(null); + runner.runScript(reader); + conn.commit(); + } + } + + TransactionFactory transactionFactory = new JdbcTransactionFactory(); + Environment environment = new Environment("development", transactionFactory, dataSource); + + Configuration configuration = new Configuration(environment); + configuration.setMapUnderscoreToCamelCase(true); + configuration.getLanguageRegistry() + .register(new ThymeleafLanguageDriver(ThymeleafLanguageDriverConfig.newInstance(c -> { + c.getTemplateFile().getPathProvider().setPrefix("sql/"); + c.getTemplateFile().getPathProvider().setIncludesPackagePath(false); + c.getTemplateFile().getPathProvider().setCacheEnabled(false); + }))); + configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); + + configuration.addMapper(TemplateFilePathProviderMapper.class); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); + } + + @Test + void testInsert() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); + Name name = new Name(); + name.setFirstName("Thymeleaf"); + name.setLastName("MyBatis"); + mapper.insert(name); + + Name loadedName = mapper.findById(name.getId()); + Assertions.assertEquals(name.getFirstName(), loadedName.getFirstName()); + Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); + } + } + + @Test + void testUpdate() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); + Name name = new Name(); + name.setFirstName("Thymeleaf"); + name.setLastName("MyBatis"); + mapper.insert(name); + + Name updatingName = new Name(); + updatingName.setId(name.getId()); + updatingName.setFirstName("Thymeleaf3"); + mapper.update(updatingName); + + Name loadedName = mapper.findById(name.getId()); + Assertions.assertEquals(updatingName.getFirstName(), loadedName.getFirstName()); + Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); + } + } + + @Test + void testDelete() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); + Name name = new Name(); + name.setFirstName("Thymeleaf"); + name.setLastName("MyBatis"); + mapper.insert(name); + + mapper.delete(name); + + Name loadedName = mapper.findById(name.getId()); + Assertions.assertNull(loadedName); + } + } + +} diff --git a/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperTest.java b/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperTest.java index 603fd26..3001c4c 100644 --- a/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperTest.java +++ b/src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperTest.java @@ -28,6 +28,7 @@ import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.hsqldb.jdbc.JDBCDataSource; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -36,11 +37,18 @@ import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig; import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; import org.mybatis.scripting.thymeleaf.integrationtest.mapper.TemplateFilePathProviderMapper; +import org.mybatis.scripting.thymeleaf.support.TemplateFilePathProvider; @DisabledIfSystemProperty(named = "mybatis.version", matches = "3\\.4\\..*|3\\.5\\.0") class TemplateFilePathProviderMapperTest { private static SqlSessionFactory sqlSessionFactory; + @BeforeAll + @AfterAll + static void cleanup() { + TemplateFilePathProvider.clearCache(); + } + @BeforeAll static void setUp() throws Exception { Class.forName("org.hsqldb.jdbcDriver"); diff --git a/src/test/resources/mybatis-thymeleaf-custom.properties b/src/test/resources/mybatis-thymeleaf-custom.properties index a5beefe..8feb648 100644 --- a/src/test/resources/mybatis-thymeleaf-custom.properties +++ b/src/test/resources/mybatis-thymeleaf-custom.properties @@ -25,6 +25,7 @@ template-file.path-provider.prefix=sqls/ template-file.path-provider.includes-package-path=false template-file.path-provider.separate-directory-per-mapper=false template-file.path-provider.includes-mapper-name-when-separate-directory=false +template-file.path-provider.cache-enabled=false dialect.prefix=mybatis dialect.like-escape-char=~ dialect.like-escape-clause-format=escape '%s'