From 4bb1495924318f7a60649278c775c8b1d70e49a6 Mon Sep 17 00:00:00 2001 From: Sanghyuk Jung Date: Thu, 12 Dec 2019 21:35:28 +0900 Subject: [PATCH] NamedParameterJdbcTemplate does not use global lock for accessing existing ParsedSql instances anymore --- .../NamedParameterJdbcTemplate.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java index 5d0e6ada4f01..bdc1e07c5339 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import javax.sql.DataSource; @@ -77,13 +78,22 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations private volatile int cacheLimit = DEFAULT_CACHE_LIMIT; - /** Cache of original SQL String to ParsedSql representation. */ + /** Fast access cache for ParsedSqls, returning already cached instances without a global lock */ + private final Map parsedSqlAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT); + + /** Cache of original SQL String to ParsedSql representation, synchronized for creation */ @SuppressWarnings("serial") - private final Map parsedSqlCache = + private final Map parsedSqlCreationCache = new LinkedHashMap(DEFAULT_CACHE_LIMIT, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > getCacheLimit(); + if (size() > getCacheLimit()) { + parsedSqlAccessCache.remove(eldest.getKey()); + return true; + } + else { + return false; + } } }; @@ -429,9 +439,19 @@ protected ParsedSql getParsedSql(String sql) { if (getCacheLimit() <= 0) { return NamedParameterUtils.parseSqlStatement(sql); } - synchronized (this.parsedSqlCache) { - return this.parsedSqlCache.computeIfAbsent(sql, NamedParameterUtils::parseSqlStatement); + + ParsedSql parsedSql = this.parsedSqlAccessCache.get(sql); + if (parsedSql == null) { + synchronized (this.parsedSqlCreationCache) { + parsedSql = this.parsedSqlCreationCache.get(sql); + if (parsedSql == null) { + parsedSql = NamedParameterUtils.parseSqlStatement(sql); + this.parsedSqlAccessCache.put(sql, parsedSql); + this.parsedSqlCreationCache.put(sql, parsedSql); + } + } } + return parsedSql; } /**