Stop using Constants utility in IsolationLevelDataSourceAdapter

See gh-30851
This commit is contained in:
Sam Brannen 2023-07-19 18:05:41 +03:00
parent 97810c84a2
commit 9571aa1c68
2 changed files with 118 additions and 14 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
@ -18,12 +18,12 @@ package org.springframework.jdbc.datasource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import org.springframework.core.Constants;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* An adapter for a target {@link javax.sql.DataSource}, applying the current
@ -47,6 +47,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* <b>Make sure that the target DataSource properly cleans up such transaction state.</b>
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 2.0.3
* @see #setIsolationLevel
* @see #setIsolationLevelName
@ -55,8 +56,18 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class IsolationLevelDataSourceAdapter extends UserCredentialsDataSourceAdapter {
/** Constants instance for TransactionDefinition. */
private static final Constants constants = new Constants(TransactionDefinition.class);
/**
* Map of constant names to constant values for the isolation constants
* defined in {@link TransactionDefinition}.
*/
static final Map<String, Integer> constants = Map.of(
"ISOLATION_DEFAULT", TransactionDefinition.ISOLATION_DEFAULT,
"ISOLATION_READ_UNCOMMITTED", TransactionDefinition.ISOLATION_READ_UNCOMMITTED,
"ISOLATION_READ_COMMITTED", TransactionDefinition.ISOLATION_READ_COMMITTED,
"ISOLATION_REPEATABLE_READ", TransactionDefinition.ISOLATION_REPEATABLE_READ,
"ISOLATION_SERIALIZABLE", TransactionDefinition.ISOLATION_SERIALIZABLE
);
@Nullable
private Integer isolationLevel;
@ -64,8 +75,8 @@ public class IsolationLevelDataSourceAdapter extends UserCredentialsDataSourceAd
/**
* Set the default isolation level by the name of the corresponding constant
* in {@link org.springframework.transaction.TransactionDefinition}, e.g.
* "ISOLATION_SERIALIZABLE".
* in {@link org.springframework.transaction.TransactionDefinition} &mdash;
* for example, {@code "ISOLATION_SERIALIZABLE"}.
* <p>If not specified, the target DataSource's default will be used.
* Note that a transaction-specific isolation value will always override
* any isolation setting specified at the DataSource level.
@ -77,10 +88,10 @@ public class IsolationLevelDataSourceAdapter extends UserCredentialsDataSourceAd
* @see #setIsolationLevel
*/
public final void setIsolationLevelName(String constantName) throws IllegalArgumentException {
if (!constantName.startsWith(DefaultTransactionDefinition.PREFIX_ISOLATION)) {
throw new IllegalArgumentException("Only isolation constants allowed");
}
setIsolationLevel(constants.asNumber(constantName).intValue());
Assert.hasText(constantName, "'constantName' must not be null or blank");
Integer isolationLevel = constants.get(constantName);
Assert.notNull(isolationLevel, "Only isolation constants allowed");
setIsolationLevel(isolationLevel);
}
/**
@ -103,9 +114,7 @@ public class IsolationLevelDataSourceAdapter extends UserCredentialsDataSourceAd
* @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionIsolationLevel()
*/
public void setIsolationLevel(int isolationLevel) {
if (!constants.getValues(DefaultTransactionDefinition.PREFIX_ISOLATION).contains(isolationLevel)) {
throw new IllegalArgumentException("Only values of isolation constants allowed");
}
Assert.isTrue(constants.containsValue(isolationLevel), "Only values of isolation constants allowed");
this.isolationLevel = (isolationLevel != TransactionDefinition.ISOLATION_DEFAULT ? isolationLevel : null);
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2002-2023 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
*
* https://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.springframework.jdbc.datasource;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.springframework.transaction.TransactionDefinition.ISOLATION_DEFAULT;
import static org.springframework.transaction.TransactionDefinition.ISOLATION_READ_COMMITTED;
/**
* Tests for {@link IsolationLevelDataSourceAdapter}.
*
* @author Sam Brannen
* @since 6.1
*/
class IsolationLevelDataSourceAdapterTests {
private final IsolationLevelDataSourceAdapter adapter = new IsolationLevelDataSourceAdapter();
@Test
void setIsolationLevelNameToUnsupportedValues() {
assertThatIllegalArgumentException().isThrownBy(() -> adapter.setIsolationLevelName(null));
assertThatIllegalArgumentException().isThrownBy(() -> adapter.setIsolationLevelName(" "));
assertThatIllegalArgumentException().isThrownBy(() -> adapter.setIsolationLevelName("bogus"));
}
/**
* Verify that the internal 'constants' map is properly configured for all
* ISOLATION_ constants defined in {@link TransactionDefinition}.
*/
@Test
void setIsolationLevelNameToAllSupportedValues() {
Set<Integer> uniqueValues = new HashSet<>();
streamIsolationConstants()
.forEach(name -> {
adapter.setIsolationLevelName(name);
Integer isolationLevel = adapter.getIsolationLevel();
if ("ISOLATION_DEFAULT".equals(name)) {
assertThat(isolationLevel).isNull();
}
else {
Integer expected = IsolationLevelDataSourceAdapter.constants.get(name);
assertThat(isolationLevel).isEqualTo(expected);
}
uniqueValues.add(isolationLevel);
});
assertThat(uniqueValues).hasSize(IsolationLevelDataSourceAdapter.constants.size());
}
@Test
void setIsolationLevel() {
assertThatIllegalArgumentException().isThrownBy(() -> adapter.setIsolationLevel(999));
adapter.setIsolationLevel(ISOLATION_DEFAULT);
assertThat(adapter.getIsolationLevel()).isNull();
adapter.setIsolationLevel(ISOLATION_READ_COMMITTED);
assertThat(adapter.getIsolationLevel()).isEqualTo(ISOLATION_READ_COMMITTED);
}
private static Stream<String> streamIsolationConstants() {
return Arrays.stream(TransactionDefinition.class.getFields())
.filter(ReflectionUtils::isPublicStaticFinal)
.map(Field::getName)
.filter(name -> name.startsWith("ISOLATION_"));
}
}