From 25287205baf85a6c381e2035afadd29d74862d1b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 12 Jan 2025 18:11:47 +0100 Subject: [PATCH] Use ReentrantLock for compilation without synchronization Closes gh-34133 --- .../jdbc/core/simple/AbstractJdbcCall.java | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java index d09c7e9e6c..71d8589a1e 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -21,6 +21,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import javax.sql.DataSource; @@ -66,6 +68,9 @@ public abstract class AbstractJdbcCall { /** List of RefCursor/ResultSet RowMapper objects. */ private final Map> declaredRowMappers = new LinkedHashMap<>(); + /** Lock for the compilation step. */ + private final Lock compilationLock = new ReentrantLock(); + /** * Has this operation been compiled? Compilation means at least checking * that a DataSource or JdbcTemplate has been provided. @@ -284,24 +289,30 @@ public abstract class AbstractJdbcCall { * @throws org.springframework.dao.InvalidDataAccessApiUsageException if the object hasn't * been correctly initialized, for example if no DataSource has been provided */ - public final synchronized void compile() throws InvalidDataAccessApiUsageException { - if (!isCompiled()) { - if (getProcedureName() == null) { - throw new InvalidDataAccessApiUsageException("Procedure or Function name is required"); - } - try { - this.jdbcTemplate.afterPropertiesSet(); - } - catch (IllegalArgumentException ex) { - throw new InvalidDataAccessApiUsageException(ex.getMessage()); - } - compileInternal(); - this.compiled = true; - if (logger.isDebugEnabled()) { - logger.debug("SqlCall for " + (isFunction() ? "function" : "procedure") + - " [" + getProcedureName() + "] compiled"); + public final void compile() throws InvalidDataAccessApiUsageException { + this.compilationLock.lock(); + try { + if (!isCompiled()) { + if (getProcedureName() == null) { + throw new InvalidDataAccessApiUsageException("Procedure or Function name is required"); + } + try { + this.jdbcTemplate.afterPropertiesSet(); + } + catch (IllegalArgumentException ex) { + throw new InvalidDataAccessApiUsageException(ex.getMessage()); + } + compileInternal(); + this.compiled = true; + if (logger.isDebugEnabled()) { + logger.debug("SqlCall for " + (isFunction() ? "function" : "procedure") + + " [" + getProcedureName() + "] compiled"); + } } } + finally { + this.compilationLock.unlock(); + } } /**