/* * Copyright 2008-2016 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 * * http://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 com.github.geequery.springdata.repository.query; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.persistence.NamedQuery; import jef.database.jpa.JefEntityManagerFactory; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethod; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import com.github.geequery.springdata.annotation.Modifying; import com.github.geequery.springdata.annotation.Procedure; import com.github.geequery.springdata.annotation.Query; import com.github.geequery.springdata.repository.support.MetamodelInformation; /** * GQ specific extension of {@link QueryMethod}. * * supports more Annotations on method.. * * @see Query * @see Modifying * @see Procedure * */ public class GqQueryMethod extends QueryMethod { private static final Set<Class<?>> NATIVE_ARRAY_TYPES; static { Set<Class<?>> types = new HashSet<Class<?>>(); types.add(byte[].class); types.add(Byte[].class); types.add(char[].class); types.add(Character[].class); NATIVE_ARRAY_TYPES = Collections.unmodifiableSet(types); } private final Method method; private final JefEntityManagerFactory emf; /** * Creates a {@link JpaQueryMethod}. * * @param method * must not be {@literal null} * @param extractor * must not be {@literal null} * @param metadata * must not be {@literal null} */ public GqQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, JefEntityManagerFactory emf) { super(method, metadata, factory); Assert.notNull(method, "Method must not be null!"); this.method = method; this.emf = emf; Assert.isTrue(!(isModifyingQuery() && getParameters().hasSpecialParameter()), String.format("Modifying method must not contain %s!", Parameters.TYPES)); assertParameterNamesInAnnotatedQuery(); } private void assertParameterNamesInAnnotatedQuery() { String annotatedQuery = getAnnotatedQuery(); if (!QueryUtils.hasNamedParameter(annotatedQuery)) { return; } for (Parameter parameter : getParameters()) { if (!parameter.isNamedParameter()) { continue; } if (!annotatedQuery.contains(String.format(":%s", parameter.getName())) && !annotatedQuery.contains(String.format("#%s", parameter.getName()))) { throw new IllegalStateException(String.format("Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!", method, parameter.getName(), annotatedQuery)); } } } /* * (non-Javadoc) * * @see * org.springframework.data.repository.query.QueryMethod#getEntityInformation * () */ @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public GqEntityMetadata<?> getEntityInformation() { return new MetamodelInformation(getDomainClass(), emf); } /** * Returns whether the finder is a modifying one. * * @return */ @Override public boolean isModifyingQuery() { return null != AnnotationUtils.findAnnotation(method, Modifying.class); } /** * Returns the actual return type of the method. * * @return */ Class<?> getReturnType() { return method.getReturnType(); } /** * Returns the query string declared in a {@link Query} annotation or * {@literal null} if neither the annotation found nor the attribute was * specified. * * @return */ String getAnnotatedQuery() { String query = getAnnotationValue("value", Query.class, String.class); return StringUtils.hasText(query) ? query : null; } /** * Returns the countQuery string declared in a {@link Query} annotation or * {@literal null} if neither the annotation found nor the attribute was * specified. * * @return */ String getCountQuery() { String countQuery = getAnnotationValue("countQuery", Query.class, String.class); return StringUtils.hasText(countQuery) ? countQuery : null; } /** * Returns the count query projection string declared in a {@link Query} * annotation or {@literal null} if neither the annotation found nor the * attribute was specified. * * @return * @since 1.6 */ String getCountQueryProjection() { String countProjection = getAnnotationValue("countProjection", Query.class, String.class); return StringUtils.hasText(countProjection) ? countProjection : null; } /** * Returns whether the backing query is a native one. * * @return */ boolean isNativeQuery() { return getAnnotationValue("nativeQuery", Query.class, Boolean.class).booleanValue(); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.query.QueryMethod#getNamedQueryName() */ @Override public String getNamedQueryName() { String annotatedName = getAnnotationValue("name", Query.class, String.class); return StringUtils.hasText(annotatedName) ? annotatedName : super.getNamedQueryName(); } /** * Returns the name of the {@link NamedQuery} that shall be used for count * queries. * * @return */ String getNamedCountQueryName() { String annotatedName = getAnnotationValue("countName", Query.class, String.class); return StringUtils.hasText(annotatedName) ? annotatedName : getNamedQueryName() + ".count"; } /** * 获得指定指定注解的字段值 * * @param attribute * 需要获得的注解字段名 * @param annotationType * 注解类型 * @param targetType * 返回值类型 * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) private <T> T getAnnotationValue(String attribute, Class annotationType, Class<T> targetType) { Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType); if (annotation == null) { return targetType.cast(AnnotationUtils.getDefaultValue(annotationType, attribute)); } return targetType.cast(AnnotationUtils.getValue(annotation, attribute)); } /** * 获得方法上的有效注解 * * @param annotationType * @return */ public <T extends Annotation> T getAnnotation(Class<T> annotationType) { return AnnotatedElementUtils.findMergedAnnotation(method, annotationType); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.query.QueryMethod#createParameters * (java.lang.reflect.Method) */ @Override protected GqParameters createParameters(Method method) { return new GqParameters(method); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.query.QueryMethod#getParameters() */ @Override public GqParameters getParameters() { return (GqParameters) super.getParameters(); } @Override public boolean isCollectionQuery() { return super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(method.getReturnType()); } /** * 通过@Procedure可以指定方法运行存储过程 * * @return */ public boolean isProcedureQuery() { return AnnotationUtils.findAnnotation(method, Procedure.class) != null; } /** * Returns whether we should clear automatically for modifying queries. * * @return */ boolean getClearAutomatically() { return getAnnotationValue("clearAutomatically", Modifying.class, Boolean.class); } }