Skip to content

Commit 5945341

Browse files
danvandeeemp911de
authored andcommitted
Implement @NativeQuery annotation.
Annotation to declare native queries directly on repository methods. NativeQuery acts as a shortcut for @query(nativeQuery = true) Closes #3155 Original pull request: #3353
1 parent 009c6a9 commit 5945341

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository;
17+
18+
import org.springframework.core.annotation.AliasFor;
19+
import org.springframework.data.annotation.QueryAnnotation;
20+
21+
import java.lang.annotation.*;
22+
23+
/**
24+
* Annotation to declare native queries directly on repository methods.
25+
* <p>
26+
* Specifically {@code @NativeQuery} is a <em>composed annotation</em> that
27+
* acts as a shortcut for {@code @Query(nativeQuery = true)}.
28+
*
29+
* @author Danny van den Elshout
30+
* @since 3.3
31+
* @see Query
32+
*/
33+
34+
@Retention(RetentionPolicy.RUNTIME)
35+
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
36+
@QueryAnnotation
37+
@Documented
38+
@Query(nativeQuery = true)
39+
public @interface NativeQuery {
40+
41+
/**
42+
* Alias for {@link Query#value()}
43+
*/
44+
@AliasFor(annotation = Query.class)
45+
String value() default "";
46+
47+
/**
48+
* Alias for {@link Query#countQuery()}
49+
*/
50+
@AliasFor(annotation = Query.class)
51+
String countQuery() default "";
52+
53+
/**
54+
* Alias for {@link Query#countProjection()}
55+
*/
56+
@AliasFor(annotation = Query.class)
57+
String countProjection() default "";
58+
59+
/**
60+
* Alias for {@link Query#name()}
61+
*/
62+
@AliasFor(annotation = Query.class)
63+
String name() default "";
64+
65+
/**
66+
* Alias for {@link Query#countName()}
67+
*/
68+
@AliasFor(annotation = Query.class)
69+
String countName() default "";
70+
71+
/**
72+
* Alias for {@link Query#queryRewriter()}
73+
*/
74+
@AliasFor(annotation = Query.class)
75+
Class<? extends QueryRewriter> queryRewriter() default QueryRewriter.IdentityQueryRewriter.class;
76+
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java

+4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@
2525

2626
/**
2727
* Annotation to declare finder queries directly on repository methods.
28+
* <p>
29+
* When using a native query {@link NativeQuery @NativeQuery} variant is available.
2830
*
2931
* @author Oliver Gierke
3032
* @author Thomas Darimont
3133
* @author Christoph Strobl
3234
* @author Greg Turnquist
35+
* @author Danny van den Elshout
3336
* @see Modifying
37+
* @see NativeQuery
3438
*/
3539
@Retention(RetentionPolicy.RUNTIME)
3640
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java

+25
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.data.domain.Sort;
4545
import org.springframework.data.jpa.domain.sample.User;
4646
import org.springframework.data.jpa.provider.QueryExtractor;
47+
import org.springframework.data.jpa.repository.NativeQuery;
4748
import org.springframework.data.jpa.repository.Query;
4849
import org.springframework.data.jpa.repository.QueryRewriter;
4950
import org.springframework.data.jpa.repository.sample.UserRepository;
@@ -69,6 +70,7 @@
6970
* @author Krzysztof Krason
7071
* @author Erik Pellizzon
7172
* @author Christoph Strobl
73+
* @author Danny van den Elshout
7274
*/
7375
@ExtendWith(MockitoExtension.class)
7476
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -164,6 +166,26 @@ void discoversNativeQuery() throws Exception {
164166
verify(em).createNativeQuery("SELECT u FROM User u WHERE u.lastname = ?1", User.class);
165167
}
166168

169+
@Test // GH-3155
170+
@SuppressWarnings({ "rawtypes", "unchecked" })
171+
void discoversNativeQueryFromNativeQueryInterface() throws Exception {
172+
173+
Method method = SampleRepository.class.getMethod("findByLastnameNativeAnnotation", String.class);
174+
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor);
175+
AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em,
176+
queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE,
177+
EVALUATION_CONTEXT_PROVIDER);
178+
179+
assertThat(jpaQuery).isInstanceOf(NativeJpaQuery.class);
180+
181+
when(em.createNativeQuery(anyString(), eq(User.class))).thenReturn(query);
182+
when(metadata.getReturnedDomainClass(method)).thenReturn((Class) User.class);
183+
184+
jpaQuery.createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { "Matthews" }));
185+
186+
verify(em).createNativeQuery("SELECT u FROM User u WHERE u.lastname = ?1", User.class);
187+
}
188+
167189
@Test // DATAJPA-554
168190
void rejectsNativeQueryWithDynamicSort() throws Exception {
169191

@@ -301,6 +323,9 @@ interface SampleRepository {
301323
@Query(value = "SELECT u FROM User u WHERE u.lastname = ?1", nativeQuery = true)
302324
List<User> findNativeByLastname(String lastname);
303325

326+
@NativeQuery(value = "SELECT u FROM User u WHERE u.lastname = ?1")
327+
List<User> findByLastnameNativeAnnotation(String lastname);
328+
304329
@Query(value = "SELECT u FROM User u WHERE u.lastname = ?1", nativeQuery = true)
305330
List<User> findNativeByLastname(String lastname, Sort sort);
306331

0 commit comments

Comments
 (0)