/* * Copyright 2014-2017 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 org.springframework.data.jpa.repository.query; import static org.hamcrest.CoreMatchers.*; 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; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.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; /** * Unit tests for {@link StoredProcedureAttributeSource}. * * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl * @since 1.6 */ @RunWith(MockitoJUnitRunner.class) public class StoredProcedureAttributeSourceUnitTests { StoredProcedureAttributeSource creator; @Mock JpaEntityMetadata<User> entityMetadata; @Before public void setup() { creator = StoredProcedureAttributeSource.INSTANCE; when(entityMetadata.getJavaType()).thenReturn(User.class); when(entityMetadata.getEntityName()).thenReturn("User"); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithImplicitProcedureName() { StoredProcedureAttributes attr = creator.createFrom(method("plus1inout", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictName() { StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedureNameValue() { StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedureNameAlias() { StoredProcedureAttributes attr = creator .createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("plus1inout")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME)); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitlyNamedProcedure() { StoredProcedureAttributes attr = creator .createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("User.plus1IO")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is("res")); } @Test // DATAJPA-455 public void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithImplicitlyNamedProcedure() { StoredProcedureAttributes attr = creator.createFrom(method("plus1", Integer.class), entityMetadata); assertThat(attr.getProcedureName(), is("User.plus1")); assertThat(attr.getOutputParameterType(), is(typeCompatibleWith(Integer.class))); assertThat(attr.getOutputParameterName(), is("res")); } @Test // DATAJPA-871 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)); } @Test // DATAJPA-871 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); } /** * @author Thomas Darimont */ static interface DummyRepository { /** * Explicitly mapped to a procedure with name "plus1inout" in database. */ @Procedure("plus1inout") // DATAJPA-455 Integer explicitlyNamedPlus1inout(Integer arg); /** * Explicitly mapped to a procedure with name "plus1inout" in database via alias. */ @Procedure(procedureName = "plus1inout") // DATAJPA-455 Integer explicitPlus1inoutViaProcedureNameAlias(Integer arg); /** * Implicitly mapped to a procedure with name "plus1inout" in database via alias. */ @Procedure // DATAJPA-455 Integer plus1inout(Integer arg); /** * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. */ @Procedure(name = "User.plus1IO") // DATAJPA-455 Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); /** * Implicitly mapped to named stored procedure "User.plus1" in {@link EntityManager}. */ @Procedure // DATAJPA-455 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 ""; } }