Polishing.
Additional tests for stored procedure out parameter detection. Update Nullability annotations and Javadoc. Reduce method visibility. See: #3460
This commit is contained in:
@@ -16,10 +16,10 @@
|
||||
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
class ProcedureParameter {
|
||||
|
||||
private final String name;
|
||||
@Nullable private final String name;
|
||||
private final int position;
|
||||
private final ParameterMode mode;
|
||||
private final Class<?> type;
|
||||
@@ -45,19 +45,28 @@ class ProcedureParameter {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
/**
|
||||
* @return the parameter name. Can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
/**
|
||||
* @return the {@code one} based parameter position as listed in
|
||||
* {@link jakarta.persistence.NamedStoredProcedureQuery#parameters()}
|
||||
* @since 3.2.6
|
||||
*/
|
||||
int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public ParameterMode getMode() {
|
||||
ParameterMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -83,6 +92,7 @@ class ProcedureParameter {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProcedureParameter{" + "name='" + name + '\'' + ", position=" + position + ", mode=" + mode + ", type=" + type + '}';
|
||||
return "ProcedureParameter{" + "name='" + name + '\'' + ", position=" + position + ", mode=" + mode + ", type="
|
||||
+ type + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,12 +123,12 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery {
|
||||
return extractOutputParameterValue(outputParameters.get(0), storedProcedureQuery);
|
||||
}
|
||||
|
||||
Map<String, Object> outputValues = new HashMap<>();
|
||||
|
||||
Map<String, Object> outputValues = new HashMap<>(outputParameters.size());
|
||||
for (ProcedureParameter outputParameter : outputParameters) {
|
||||
outputValues.put(
|
||||
!outputParameter.getName().isEmpty() ? outputParameter.getName() : outputParameter.getPosition() + "",
|
||||
extractOutputParameterValue(outputParameter, storedProcedureQuery));
|
||||
|
||||
String param = StringUtils.hasText(outputParameter.getName()) ? outputParameter.getName()
|
||||
: Integer.toString(outputParameter.getPosition());
|
||||
outputValues.put(param, extractOutputParameterValue(outputParameter, storedProcedureQuery));
|
||||
}
|
||||
|
||||
return outputValues;
|
||||
|
||||
@@ -15,21 +15,29 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
import jakarta.persistence.StoredProcedureParameter;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
@@ -58,14 +66,14 @@ import org.springframework.util.ReflectionUtils;
|
||||
class StoredProcedureAttributeSourceUnitTests {
|
||||
|
||||
private StoredProcedureAttributeSource creator;
|
||||
@Mock JpaEntityMetadata<User> entityMetadata;
|
||||
@Mock JpaEntityMetadata<?> entityMetadata;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
|
||||
creator = StoredProcedureAttributeSource.INSTANCE;
|
||||
|
||||
when(entityMetadata.getJavaType()).thenReturn(User.class);
|
||||
doReturn(User.class).when(entityMetadata).getJavaType();
|
||||
when(entityMetadata.getEntityName()).thenReturn("User");
|
||||
}
|
||||
|
||||
@@ -325,6 +333,34 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
assertThat(outputParameter.getName()).isEqualTo("dummies");
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3463
|
||||
@ValueSource(
|
||||
strings = { "inInOut", "inoutInOut", "inoutOut", "outInIn", "inInInoutOut", "inInoutInOut", "inInoutInoutOut" })
|
||||
void storedProcedureParameterInoutAndOutParameterPositionDetection(String methodName) {
|
||||
|
||||
String[] paramPattern = methodName.split("(?=[A-Z])");
|
||||
Class<?>[] methodArgs = new Class<?>[paramPattern.length - 1];
|
||||
Arrays.fill(methodArgs, Integer.class);
|
||||
|
||||
List<Integer> expectedOut = new ArrayList<>(2);
|
||||
int position = 0;
|
||||
for (String s : paramPattern) {
|
||||
position++;
|
||||
switch (s.toLowerCase()) {
|
||||
case "inout", "out" -> expectedOut.add(position);
|
||||
}
|
||||
}
|
||||
|
||||
doReturn(InOut.class).when(entityMetadata).getJavaType();
|
||||
when(entityMetadata.getEntityName()).thenReturn("InOut");
|
||||
|
||||
StoredProcedureAttributes attr = creator.createFrom(method(methodName, methodArgs), entityMetadata);
|
||||
assertThat(attr.getOutputProcedureParameters()).extracting(ProcedureParameter::getPosition) //
|
||||
.withFailMessage("Expecting method %s to have %s out parameters at positions %s but was %s.", methodName,
|
||||
expectedOut.size(), expectedOut, attr.getOutputProcedureParameters()) //
|
||||
.isEqualTo(expectedOut);
|
||||
}
|
||||
|
||||
private static Method method(String name, Class<?>... paramTypes) {
|
||||
return ReflectionUtils.findMethod(DummyRepository.class, name, paramTypes);
|
||||
}
|
||||
@@ -410,6 +446,27 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
|
||||
@Procedure(value = "1_input_1_resultset", outputParameterName = "dummies", refCursor = true) // DATAJPA-1657
|
||||
List<Dummy> entityListFromResultSetWithInputAndNamedOutputAndCursor(Integer arg);
|
||||
|
||||
@Procedure(name = "InOut.in_in_out")
|
||||
Map<Object, Object> inInOut(Integer in1, Integer in2);
|
||||
|
||||
@Procedure(name = "InOut.inout_in_out")
|
||||
Map<Object, Object> inoutInOut(Integer inout1, Integer in2);
|
||||
|
||||
@Procedure(name = "InOut.inout_out")
|
||||
Map<Object, Object> inoutOut(Integer inout1);
|
||||
|
||||
@Procedure(name = "InOut.out_in_in")
|
||||
Map<Object, Object> outInIn(Integer in1, Integer in2);
|
||||
|
||||
@Procedure(name = "InOut.in_in_inout_out")
|
||||
Map<Object, Object> inInInoutOut(Integer in1, Integer in2, Integer inout);
|
||||
|
||||
@Procedure(name = "InOut.in_inout_in_out")
|
||||
Map<Object, Object> inInoutInOut(Integer in1, Integer inout, Integer in2);
|
||||
|
||||
@Procedure(name = "InOut.in_inout_inout_out")
|
||||
Map<Object, Object> inInoutInoutOut(Integer in1, Integer inout1, Integer inout2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -429,4 +486,52 @@ class StoredProcedureAttributeSourceUnitTests {
|
||||
@AliasFor(annotation = Procedure.class, attribute = "outputParameterName")
|
||||
String outParamName() default "";
|
||||
}
|
||||
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.in_in_out", //
|
||||
procedureName = "positional_in_in_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.inout_in_out", //
|
||||
procedureName = "positional_inout_in_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.inout_out", //
|
||||
procedureName = "positional_inout_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.out_in_in", //
|
||||
procedureName = "positional_out_in_in", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.in_in_inout_out", //
|
||||
procedureName = "positional_in_in_inout_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.in_inout_in_out", //
|
||||
procedureName = "positional_in_inout_in_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
@NamedStoredProcedureQuery( //
|
||||
name = "InOut.in_inout_inout_out", //
|
||||
procedureName = "positional_in_inout_inout_out", //
|
||||
parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.INOUT, type = Integer.class),
|
||||
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class) })
|
||||
private static class InOut {
|
||||
@Id private Long id;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user