diff --git a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java index 7a2bc5452..8126c6016 100644 --- a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java +++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-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. @@ -30,6 +30,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryMethod; * * Since 1.9 we support the definition of dynamic {@link EntityGraph}s by allowing to customize the fetch-graph via * via {@link #attributePaths()} ad-hoc fetch-graph configuration. + * @author Christoph Strobl * * If {@link #attributePaths()} are specified then we ignore the entity-graph name {@link #value()} * and treat this {@link EntityGraph} as dynamic. @@ -38,7 +39,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryMethod; * @since 1.6 */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Documented public @interface EntityGraph { diff --git a/src/main/java/org/springframework/data/jpa/repository/Modifying.java b/src/main/java/org/springframework/data/jpa/repository/Modifying.java index 069f5090a..1085ae88c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/Modifying.java +++ b/src/main/java/org/springframework/data/jpa/repository/Modifying.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 the original author or authors. + * 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. @@ -25,9 +25,10 @@ import java.lang.annotation.Target; * Indicates a method should be regarded as modifying query. * * @author Oliver Gierke + * @author Christoph Strobl */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Documented public @interface Modifying { diff --git a/src/main/java/org/springframework/data/jpa/repository/Query.java b/src/main/java/org/springframework/data/jpa/repository/Query.java index ecc0d7772..5fe75c1e2 100644 --- a/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * 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. @@ -28,9 +28,10 @@ import org.springframework.data.annotation.QueryAnnotation; * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @QueryAnnotation @Documented public @interface Query { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index 80ee3a9dd..75685eaa8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-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. @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.Entity; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -24,6 +25,7 @@ import org.springframework.util.StringUtils; * Default implementation for {@link JpaEntityMetadata}. * * @author Oliver Gierke + * @author Christoph Strobl */ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { @@ -55,7 +57,7 @@ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { */ public String getEntityName() { - Entity entity = domainType.getAnnotation(Entity.class); + Entity entity = AnnotatedElementUtils.findMergedAnnotation(domainType, Entity.class); boolean hasName = null != entity && StringUtils.hasText(entity.name()); return hasName ? entity.name() : domainType.getSimpleName(); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index c1bc2dc7c..6dd214652 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 the original author or authors. + * 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. @@ -15,8 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.core.annotation.AnnotationUtils.*; - +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -28,6 +27,7 @@ import java.util.Set; import javax.persistence.LockModeType; import javax.persistence.QueryHint; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.EntityGraph; @@ -48,6 +48,7 @@ import org.springframework.util.StringUtils; * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ public class JpaQueryMethod extends QueryMethod { @@ -135,7 +136,7 @@ public class JpaQueryMethod extends QueryMethod { @Override public boolean isModifyingQuery() { - return null != method.getAnnotation(Modifying.class); + return null != AnnotationUtils.findAnnotation(method, Modifying.class); } /** @@ -147,7 +148,7 @@ public class JpaQueryMethod extends QueryMethod { List result = new ArrayList(); - QueryHints hints = getAnnotation(method, QueryHints.class); + QueryHints hints = AnnotatedElementUtils.findMergedAnnotation(method, QueryHints.class); if (hints != null) { result.addAll(Arrays.asList(hints.value())); } @@ -162,7 +163,7 @@ public class JpaQueryMethod extends QueryMethod { */ LockModeType getLockModeType() { - Lock annotation = findAnnotation(method, Lock.class); + Lock annotation = AnnotatedElementUtils.findMergedAnnotation(method, Lock.class); return (LockModeType) AnnotationUtils.getValue(annotation); } @@ -174,7 +175,7 @@ public class JpaQueryMethod extends QueryMethod { */ JpaEntityGraph getEntityGraph() { - EntityGraph annotation = findAnnotation(method, EntityGraph.class); + EntityGraph annotation = AnnotatedElementUtils.findMergedAnnotation(method, EntityGraph.class); return annotation == null ? null : new JpaEntityGraph(annotation, getNamedQueryName()); } @@ -186,7 +187,7 @@ public class JpaQueryMethod extends QueryMethod { */ boolean applyHintsToCountQuery() { - QueryHints hints = getAnnotation(method, QueryHints.class); + QueryHints hints = AnnotatedElementUtils.findMergedAnnotation(method, QueryHints.class); return hints != null ? hints.forCounting() : false; } @@ -284,8 +285,7 @@ public class JpaQueryMethod extends QueryMethod { * @return */ boolean getClearAutomatically() { - - return (Boolean) AnnotationUtils.getValue(method.getAnnotation(Modifying.class), "clearAutomatically"); + return getMergedOrDefaultAnnotationValue("clearAutomatically", Modifying.class, Boolean.class); } /** @@ -298,12 +298,18 @@ public class JpaQueryMethod extends QueryMethod { * @return */ private T getAnnotationValue(String attribute, Class type) { + return getMergedOrDefaultAnnotationValue(attribute, Query.class, type); + } - Query annotation = method.getAnnotation(Query.class); - Object value = annotation == null ? AnnotationUtils.getDefaultValue(Query.class, attribute) - : AnnotationUtils.getValue(annotation, attribute); + @SuppressWarnings({ "rawtypes", "unchecked" }) + private T getMergedOrDefaultAnnotationValue(String attribute, Class annotationType, Class targetType) { - return type.cast(value); + Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType); + if (annotation == null) { + return targetType.cast(AnnotationUtils.getDefaultValue(annotationType, attribute)); + } + + return targetType.cast(AnnotationUtils.getValue(annotation, attribute)); } /* @@ -339,7 +345,7 @@ public class JpaQueryMethod extends QueryMethod { * @return */ public boolean isProcedureQuery() { - return method.getAnnotation(Procedure.class) != null; + return AnnotationUtils.findAnnotation(method, Procedure.class) != null; } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java index 3cd8efe36..fef9de464 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -25,9 +25,10 @@ import java.lang.annotation.Target; * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl * @since 1.6 */ -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface Procedure { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 47fef757c..751c71f1c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-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. @@ -24,6 +24,7 @@ import javax.persistence.NamedStoredProcedureQueries; import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.StoredProcedureParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -32,6 +33,7 @@ import org.springframework.util.StringUtils; * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl * @since 1.6 */ enum StoredProcedureAttributeSource { @@ -50,7 +52,7 @@ enum StoredProcedureAttributeSource { Assert.notNull(method, "Method must not be null!"); Assert.notNull(entityMetadata, "EntityMetadata must not be null!"); - Procedure procedure = method.getAnnotation(Procedure.class); + Procedure procedure = AnnotatedElementUtils.findMergedAnnotation(method, Procedure.class); Assert.notNull(procedure, "Method must have an @Procedure annotation!"); NamedStoredProcedureQuery namedStoredProc = tryFindAnnotatedNamedStoredProcedureQuery(method, entityMetadata, @@ -201,12 +203,14 @@ enum StoredProcedureAttributeSource { List queries = new ArrayList(); - NamedStoredProcedureQueries namedQueriesAnnotation = entityType.getAnnotation(NamedStoredProcedureQueries.class); + NamedStoredProcedureQueries namedQueriesAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType, + NamedStoredProcedureQueries.class); if (namedQueriesAnnotation != null) { queries.addAll(Arrays.asList(namedQueriesAnnotation.value())); } - NamedStoredProcedureQuery namedQueryAnnotation = entityType.getAnnotation(NamedStoredProcedureQuery.class); + NamedStoredProcedureQuery namedQueryAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType, + NamedStoredProcedureQuery.class); if (namedQueryAnnotation != null) { queries.add(namedQueryAnnotation); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index d1fbea738..e099fc57f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-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. @@ -31,6 +31,7 @@ import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; @@ -48,6 +49,7 @@ import org.springframework.util.ClassUtils; * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, BeanClassLoaderAware { @@ -164,19 +166,19 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B } private static EntityGraph findEntityGraph(Method method) { - return AnnotationUtils.findAnnotation(method, EntityGraph.class); + return AnnotatedElementUtils.findMergedAnnotation(method, EntityGraph.class); } private static LockModeType findLockModeType(Method method) { - Lock annotation = AnnotationUtils.findAnnotation(method, Lock.class); + Lock annotation = AnnotatedElementUtils.findMergedAnnotation(method, Lock.class); return annotation == null ? null : (LockModeType) AnnotationUtils.getValue(annotation); } private static Map findQueryHints(Method method) { Map queryHints = new HashMap(); - QueryHints queryHintsAnnotation = AnnotationUtils.findAnnotation(method, QueryHints.class); + QueryHints queryHintsAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, QueryHints.class); if (queryHintsAnnotation != null) { diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 0e176f515..4faaa3904 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2011 the original author or authors. + * 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. @@ -33,6 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.annotation.AliasFor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -59,6 +60,7 @@ import org.springframework.data.repository.query.QueryMethod; * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public class JpaQueryMethodUnitTests { @@ -393,6 +395,148 @@ public class JpaQueryMethodUnitTests { getQueryMethod(ValidRepository.class, "queryWithPositionalBinding", String.class); } + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForLockLockMode() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getLockModeType(), is(LockModeType.PESSIMISTIC_FORCE_INCREMENT)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryHints() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getHints(), hasSize(1)); + assertThat(method.getHints().get(0).name(), is("foo")); + assertThat(method.getHints().get(0).value(), is("bar")); + + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryHintsCounting() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.applyHintsToCountQuery(), is(true)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForModifyingClearAutomatically() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.isModifyingQuery(), is(true)); + assertThat(method.getClearAutomatically(), is(true)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForHintsApplyToCountQuery() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.applyHintsToCountQuery(), is(true)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryValue() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getAnnotatedQuery(), is(equalTo("select u from User u where u.firstname = ?1"))); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryCountQuery() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getCountQuery(), is(equalTo("select u from User u where u.lastname = ?1"))); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryCountQueryProjection() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getCountQueryProjection(), is(equalTo("foo-bar"))); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryNamedQueryName() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getNamedQueryName(), is(equalTo("namedQueryName"))); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryNamedCountQueryName() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.getNamedCountQueryName(), is(equalTo("namedCountQueryName"))); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForQueryNativeQuery() throws Exception { + + JpaQueryMethod method = getQueryMethod(ValidRepository.class, "withMetaAnnotationUsingAliasFor"); + + assertThat(method.isNativeQuery(), is(true)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void usesAliasedValueForEntityGraph() throws Exception { + + doReturn(User.class).when(metadata).getDomainType(); + doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); + + JpaQueryMethod method = new JpaQueryMethod( + JpaRepositoryOverride.class.getMethod("getOneWithCustomEntityGraphAnnotation"), metadata, factory, extractor); + + assertThat(method.getEntityGraph(), is(notNullValue())); + assertThat(method.getEntityGraph().getName(), is("User.detail")); + assertThat(method.getEntityGraph().getType(), is(EntityGraphType.LOAD)); + } + /** * Interface to define invalid repository methods for testing. * @@ -456,6 +600,9 @@ public class JpaQueryMethodUnitTests { @Query("select u from User u where u.firstname = ?1") User queryWithPositionalBinding(@Param("firstname") String firstname); + + @CustomComposedAnnotationWithAliasFor + void withMetaAnnotationUsingAliasFor(); } static interface JpaRepositoryOverride extends JpaRepository { @@ -478,6 +625,10 @@ public class JpaQueryMethodUnitTests { */ @EntityGraph User getOneById(Long id); + + @CustomComposedEntityGraphAnnotationWithAliasFor + User getOneWithCustomEntityGraphAnnotation(); + } @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) @@ -486,4 +637,57 @@ public class JpaQueryMethodUnitTests { static @interface CustomAnnotation { } + + @Modifying + @Query + @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) + @QueryHints(@QueryHint(name = "foo", value = "bar")) + @Retention(RetentionPolicy.RUNTIME) + static @interface CustomComposedAnnotationWithAliasFor { + + @AliasFor(annotation = Modifying.class, attribute = "clearAutomatically") + boolean doClear() default true; + + @AliasFor(annotation = Query.class, attribute = "value") + String querystring() default "select u from User u where u.firstname = ?1"; + + @AliasFor(annotation = Query.class, attribute = "countQuery") + String countQueryString() default "select u from User u where u.lastname = ?1"; + + @AliasFor(annotation = Query.class, attribute = "countProjection") + String countProjectionString() default "foo-bar"; + + @AliasFor(annotation = Query.class, attribute = "nativeQuery") + boolean isNativeQuery() default true; + + @AliasFor(annotation = Query.class, attribute = "name") + String namedQueryName() default "namedQueryName"; + + @AliasFor(annotation = Query.class, attribute = "countName") + String namedCountQueryName() default "namedCountQueryName"; + + @AliasFor(annotation = Lock.class, attribute = "value") + LockModeType lock() default LockModeType.PESSIMISTIC_FORCE_INCREMENT; + + @AliasFor(annotation = QueryHints.class, attribute = "value") + QueryHint[] hints() default @QueryHint(name = "foo", value = "bar") + ; + + @AliasFor(annotation = QueryHints.class, attribute = "forCounting") + boolean doCount() default true; + } + + @EntityGraph + @Retention(RetentionPolicy.RUNTIME) + static @interface CustomComposedEntityGraphAnnotationWithAliasFor { + + @AliasFor(annotation = EntityGraph.class, attribute = "value") + String graphName() default "User.detail"; + + @AliasFor(annotation = EntityGraph.class, attribute = "type") + EntityGraphType graphType() default EntityGraphType.LOAD; + + @AliasFor(annotation = EntityGraph.class, attribute = "attributePaths") + String[] paths() default { "foo", "bar" }; + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index 816681acf..c947c4d22 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -20,6 +20,8 @@ import static org.hamcrest.object.IsCompatibleType.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import javax.persistence.EntityManager; @@ -29,6 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.annotation.AliasFor; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.repository.query.Param; import org.springframework.util.ReflectionUtils; @@ -38,6 +41,7 @@ import org.springframework.util.ReflectionUtils; * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl * @since 1.6 */ @RunWith(MockitoJUnitRunner.class) @@ -102,8 +106,8 @@ public class StoredProcedureAttributeSourceUnitTests { @Test public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedureNameAlias() { - StoredProcedureAttributes attr = creator.createFrom( - method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata); + StoredProcedureAttributes attr = creator + .createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); @@ -116,8 +120,8 @@ public class StoredProcedureAttributeSourceUnitTests { @Test public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitlyNamedProcedure() { - StoredProcedureAttributes attr = creator.createFrom( - method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata); + StoredProcedureAttributes attr = creator + .createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("User.plus1IO")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); @@ -137,6 +141,34 @@ public class StoredProcedureAttributeSourceUnitTests { assertThat(attr.getOutputParameterName(), is("res")); } + /** + * @see DATAJPA-871 + */ + @Test + public void aliasedStoredProcedure() { + + StoredProcedureAttributes attr = creator + .createFrom(method("plus1inoutWithComposedAnnotationOverridingProcedureName", Integer.class), entityMetadata); + + assertThat(attr.getProcedureName(), is(equalTo("plus1inout"))); + assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); + assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); + } + + /** + * @see DATAJPA-871 + */ + @Test + public void aliasedStoredProcedure2() { + + StoredProcedureAttributes attr = creator + .createFrom(method("plus1inoutWithComposedAnnotationOverridingName", Integer.class), entityMetadata); + + assertThat(attr.getProcedureName(), is(equalTo("User.plus1"))); + assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); + assertThat(attr.getOutputParameterName(), is(equalTo("res"))); + } + private static Method method(String name, Class... paramTypes) { return ReflectionUtils.findMethod(DummyRepository.class, name, paramTypes); } @@ -185,5 +217,28 @@ public class StoredProcedureAttributeSourceUnitTests { */ @Procedure Integer plus1(@Param("arg") Integer arg); + + @ComposedProcedureUsingAliasFor(explicitProcedureName = "plus1inout") + Integer plus1inoutWithComposedAnnotationOverridingProcedureName(Integer arg); + + @ComposedProcedureUsingAliasFor(emProcedureName = "User.plus1") + Integer plus1inoutWithComposedAnnotationOverridingName(Integer arg); + } + + @Procedure + @Retention(RetentionPolicy.RUNTIME) + static @interface ComposedProcedureUsingAliasFor { + + @AliasFor(annotation = Procedure.class, attribute = "value") + String dbProcedureName() default ""; + + @AliasFor(annotation = Procedure.class, attribute = "procedureName") + String explicitProcedureName() default ""; + + @AliasFor(annotation = Procedure.class, attribute = "name") + String emProcedureName() default ""; + + @AliasFor(annotation = Procedure.class, attribute = "outputParameterName") + String outParamName() default ""; } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index 3c99ac06d..f314efe39 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-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. @@ -18,15 +18,20 @@ package org.springframework.data.jpa.repository.support; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + import javax.persistence.Entity; import org.junit.Test; +import org.springframework.core.annotation.AliasFor; import org.springframework.data.jpa.repository.query.DefaultJpaEntityMetadata; /** * Unit tests for {@link DefaultJpaEntityMetadata}. * * @author Oliver Gierke + * @author Christoph Strobl */ public class DefaultJpaEntityMetadataUnitTest { @@ -57,8 +62,30 @@ public class DefaultJpaEntityMetadataUnitTest { assertThat(metadata.getEntityName(), is("Entity")); } + /** + * @see DATAJPA-871 + */ + @Test + public void returnsCustomizedEntityNameIfConfiguredViaComposedAnnotation() { + + DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata( + BarWithComposedAnnotation.class); + assertThat(metadata.getEntityName(), is("Entity")); + } + static class Foo {} @Entity(name = "Entity") static class Bar {} + + @CustomEntityAnnotationUsingAliasFor(entityName = "Entity") + static class BarWithComposedAnnotation {} + + @Entity + @Retention(RetentionPolicy.RUNTIME) + static @interface CustomEntityAnnotationUsingAliasFor { + + @AliasFor(annotation = Entity.class, attribute = "name") + String entityName(); + } }