package org.springframework.data.jpa.repository.query;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.NamedStoredProcedureQueries;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.StoredProcedureParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
enum StoredProcedureAttributeSource {
INSTANCE;
public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata<?> entityMetadata) {
Assert.notNull(method, "Method must not be null!");
Assert.notNull(entityMetadata, "EntityMetadata must not be null!");
Procedure procedure = AnnotatedElementUtils.findMergedAnnotation(method, Procedure.class);
Assert.notNull(procedure, "Method must have an @Procedure annotation!");
NamedStoredProcedureQuery namedStoredProc = tryFindAnnotatedNamedStoredProcedureQuery(method, entityMetadata,
procedure);
if (namedStoredProc != null) {
return newProcedureAttributesFrom(method, namedStoredProc, procedure);
}
String procedureName = deriveProcedureNameFrom(method, procedure);
if (StringUtils.isEmpty(procedureName)) {
throw new IllegalArgumentException("Could not determine name of procedure for @Procedure annotated method: "
+ method);
}
return new StoredProcedureAttributes(procedureName, procedure.outputParameterName(), method.getReturnType());
}
private String deriveProcedureNameFrom(Method method, Procedure procedure) {
if (StringUtils.hasText(procedure.value())) {
return procedure.value();
}
String procedureName = procedure.procedureName();
return StringUtils.hasText(procedureName) ? procedureName : method.getName();
}
private StoredProcedureAttributes newProcedureAttributesFrom(Method method,
NamedStoredProcedureQuery namedStoredProc, Procedure procedure) {
List<String> outputParameterNames = new ArrayList<>();
List<Class<?>> outputParameterTypes = new ArrayList<>();
if (!procedure.outputParameterName().isEmpty()) {
outputParameterNames.add(procedure.outputParameterName());
} else {
List<StoredProcedureParameter> outputParameters = extractOutputParametersFrom(namedStoredProc);
for (StoredProcedureParameter outputParameter : outputParameters) {
outputParameterNames.add(outputParameter.name());
outputParameterTypes.add(outputParameter.type());
}
}
if (outputParameterTypes.isEmpty()) {
outputParameterTypes.add(method.getReturnType());
}
return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterNames, outputParameterTypes, true);
}
private List<StoredProcedureParameter> (NamedStoredProcedureQuery namedStoredProc) {
List<StoredProcedureParameter> outputParameters = new ArrayList<StoredProcedureParameter>();
for (StoredProcedureParameter param : namedStoredProc.parameters()) {
switch (param.mode()) {
case OUT:
case INOUT:
case REF_CURSOR:
outputParameters.add(param);
break;
case IN:
default:
continue;
}
}
return outputParameters;
}
@Nullable
private NamedStoredProcedureQuery tryFindAnnotatedNamedStoredProcedureQuery(Method method,
JpaEntityMetadata<?> entityMetadata, Procedure procedure) {
Assert.notNull(method, "Method must not be null!");
Assert.notNull(entityMetadata, "EntityMetadata must not be null!");
Assert.notNull(procedure, "Procedure must not be null!");
Class<?> entityType = entityMetadata.getJavaType();
List<NamedStoredProcedureQuery> queries = collectNamedStoredProcedureQueriesFrom(entityType);
if (queries.isEmpty()) {
return null;
}
String namedProcedureName = derivedNamedProcedureNameFrom(method, entityMetadata, procedure);
for (NamedStoredProcedureQuery query : queries) {
if (query.name().equals(namedProcedureName)) {
return query;
}
}
return null;
}
private String derivedNamedProcedureNameFrom(Method method, JpaEntityMetadata<?> entityMetadata, Procedure procedure) {
return StringUtils.hasText(procedure.name()) ? procedure.name() : entityMetadata.getEntityName() + "."
+ method.getName();
}
private List<NamedStoredProcedureQuery> collectNamedStoredProcedureQueriesFrom(Class<?> entityType) {
List<NamedStoredProcedureQuery> queries = new ArrayList<NamedStoredProcedureQuery>();
NamedStoredProcedureQueries namedQueriesAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType,
NamedStoredProcedureQueries.class);
if (namedQueriesAnnotation != null) {
queries.addAll(Arrays.asList(namedQueriesAnnotation.value()));
}
NamedStoredProcedureQuery namedQueryAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType,
NamedStoredProcedureQuery.class);
if (namedQueryAnnotation != null) {
queries.add(namedQueryAnnotation);
}
return queries;
}
}