DATAJPA-871 - Allow usage of composed annotations using @AliasFor.

We allow and support composing annotations for: @Entity, @EntityGraph, @Lock, @Modifying, @Query, @QueryHints as well as @Procedure.

Original pull request: #166.
This commit is contained in:
Christoph Strobl
2016-03-14 15:42:20 +01:00
committed by Oliver Gierke
parent 154e3370cf
commit a1509f9517
11 changed files with 344 additions and 40 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<T> implements JpaEntityMetadata<T> {
@@ -55,7 +57,7 @@ public class DefaultJpaEntityMetadata<T> implements JpaEntityMetadata<T> {
*/
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();

View File

@@ -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<QueryHint> result = new ArrayList<QueryHint>();
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> T getAnnotationValue(String attribute, Class<T> 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> T getMergedOrDefaultAnnotationValue(String attribute, Class annotationType, Class<T> 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;
}
/**

View File

@@ -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 {

View File

@@ -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<NamedStoredProcedureQuery> queries = new ArrayList<NamedStoredProcedureQuery>();
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);
}

View File

@@ -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<String, Object> findQueryHints(Method method) {
Map<String, Object> queryHints = new HashMap<String, Object>();
QueryHints queryHintsAnnotation = AnnotationUtils.findAnnotation(method, QueryHints.class);
QueryHints queryHintsAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, QueryHints.class);
if (queryHintsAnnotation != null) {

View File

@@ -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<User, Long> {
@@ -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" };
}
}

View File

@@ -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 "";
}
}

View File

@@ -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<BarWithComposedAnnotation> metadata = new DefaultJpaEntityMetadata<BarWithComposedAnnotation>(
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();
}
}