1
16 package org.springframework.data.jpa.repository.query;
17
18 import java.lang.reflect.Method;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22
23 import javax.persistence.NamedStoredProcedureQueries;
24 import javax.persistence.NamedStoredProcedureQuery;
25 import javax.persistence.StoredProcedureParameter;
26
27 import org.springframework.core.annotation.AnnotatedElementUtils;
28 import org.springframework.lang.Nullable;
29 import org.springframework.util.Assert;
30 import org.springframework.util.StringUtils;
31
32
43 enum StoredProcedureAttributeSource {
44
45 INSTANCE;
46
47
54 public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata<?> entityMetadata) {
55
56 Assert.notNull(method, "Method must not be null!");
57 Assert.notNull(entityMetadata, "EntityMetadata must not be null!");
58
59 Procedure procedure = AnnotatedElementUtils.findMergedAnnotation(method, Procedure.class);
60 Assert.notNull(procedure, "Method must have an @Procedure annotation!");
61
62 NamedStoredProcedureQuery namedStoredProc = tryFindAnnotatedNamedStoredProcedureQuery(method, entityMetadata,
63 procedure);
64
65 if (namedStoredProc != null) {
66 return newProcedureAttributesFrom(method, namedStoredProc, procedure);
67 }
68
69 String procedureName = deriveProcedureNameFrom(method, procedure);
70 if (StringUtils.isEmpty(procedureName)) {
71 throw new IllegalArgumentException("Could not determine name of procedure for @Procedure annotated method: "
72 + method);
73 }
74
75 return new StoredProcedureAttributes(procedureName, procedure.outputParameterName(), method.getReturnType());
76 }
77
78
86 private String deriveProcedureNameFrom(Method method, Procedure procedure) {
87
88 if (StringUtils.hasText(procedure.value())) {
89 return procedure.value();
90 }
91
92 String procedureName = procedure.procedureName();
93 return StringUtils.hasText(procedureName) ? procedureName : method.getName();
94 }
95
96
102 private StoredProcedureAttributes newProcedureAttributesFrom(Method method,
103 NamedStoredProcedureQuery namedStoredProc, Procedure procedure) {
104
105 List<String> outputParameterNames = new ArrayList<>();
106 List<Class<?>> outputParameterTypes = new ArrayList<>();
107
108 if (!procedure.outputParameterName().isEmpty()) {
109
110 outputParameterNames.add(procedure.outputParameterName());
111 } else {
112
113
114 List<StoredProcedureParameter> outputParameters = extractOutputParametersFrom(namedStoredProc);
115
116 for (StoredProcedureParameter outputParameter : outputParameters) {
117 outputParameterNames.add(outputParameter.name());
118 outputParameterTypes.add(outputParameter.type());
119 }
120 }
121
122 if (outputParameterTypes.isEmpty()) {
123 outputParameterTypes.add(method.getReturnType());
124 }
125
126 return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterNames, outputParameterTypes, true);
127 }
128
129 private List<StoredProcedureParameter> extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) {
130
131 List<StoredProcedureParameter> outputParameters = new ArrayList<StoredProcedureParameter>();
132
133 for (StoredProcedureParameter param : namedStoredProc.parameters()) {
134
135 switch (param.mode()) {
136 case OUT:
137 case INOUT:
138 case REF_CURSOR:
139 outputParameters.add(param);
140 break;
141 case IN:
142 default:
143 continue;
144 }
145 }
146
147 return outputParameters;
148 }
149
150
156 @Nullable
157 private NamedStoredProcedureQuery tryFindAnnotatedNamedStoredProcedureQuery(Method method,
158 JpaEntityMetadata<?> entityMetadata, Procedure procedure) {
159
160 Assert.notNull(method, "Method must not be null!");
161 Assert.notNull(entityMetadata, "EntityMetadata must not be null!");
162 Assert.notNull(procedure, "Procedure must not be null!");
163
164 Class<?> entityType = entityMetadata.getJavaType();
165
166 List<NamedStoredProcedureQuery> queries = collectNamedStoredProcedureQueriesFrom(entityType);
167
168 if (queries.isEmpty()) {
169 return null;
170 }
171
172 String namedProcedureName = derivedNamedProcedureNameFrom(method, entityMetadata, procedure);
173
174 for (NamedStoredProcedureQuery query : queries) {
175
176 if (query.name().equals(namedProcedureName)) {
177 return query;
178 }
179 }
180
181 return null;
182 }
183
184
190 private String derivedNamedProcedureNameFrom(Method method, JpaEntityMetadata<?> entityMetadata, Procedure procedure) {
191 return StringUtils.hasText(procedure.name()) ? procedure.name() : entityMetadata.getEntityName() + "."
192 + method.getName();
193 }
194
195
199 private List<NamedStoredProcedureQuery> collectNamedStoredProcedureQueriesFrom(Class<?> entityType) {
200
201 List<NamedStoredProcedureQuery> queries = new ArrayList<NamedStoredProcedureQuery>();
202
203 NamedStoredProcedureQueries namedQueriesAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType,
204 NamedStoredProcedureQueries.class);
205 if (namedQueriesAnnotation != null) {
206 queries.addAll(Arrays.asList(namedQueriesAnnotation.value()));
207 }
208
209 NamedStoredProcedureQuery namedQueryAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType,
210 NamedStoredProcedureQuery.class);
211 if (namedQueryAnnotation != null) {
212 queries.add(namedQueryAnnotation);
213 }
214
215 return queries;
216 }
217 }
218