Hello, Hablochitel!
While developing a Spring Boot 2 training project, I decided to experiment with @Param
in Spring Data JPA queries, or rather the lack of them :
@Transactional(readOnly = true) public interface UserRepository extends JpaRepository<User, Integer> { @Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") Optional<User> findByEmailIgnoreCase(@Param("email") String email); List<User> findByLastNameContainingIgnoreCase(@Param("lastname") String lastName); }
(about magic, how the second method works in the old publication In the footsteps of the Spring Pet Clinic ).
@Param
removing @Param
you can make sure that Spring works fine without them . I heard about the parameter in the compilation, which allows you not to duplicate the names in the annotations, but I did not do anything special, so I decided dig deeper podebazhit.
If you still use annotations from the article title, Spring Boot and JDK 8, please use the cat:
The first thing I tried was to change the name in the parameter ( mail
instead of email
):
@Query("SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)") Optional<User> findByEmailIgnoreCase(String mail);
I get a welcome and a place to breakpoint:
Caused by: java.lang.IllegalStateException: Using named parameters for method public abstract java.util.Optional ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase(java.lang.String) but parameter 'Optional[mail]' not found in annotated query 'SELECT u FROM User u WHERE LOWER(u.email) = LOWER(:email)'! at org.springframework.data.jpa.repository.query.JpaQueryMethod.assertParameterNamesInAnnotatedQuery(JpaQueryMethod.java:125) ~[spring-data-jpa-2.1.3.RELEASE.jar:2.1.3.RELEASE]
Next we find the place where the name of the method parameter is determined:
You can see that 2 strategies are used: StandardReflectionParameterNameDiscoverer
and LocalVariableTableParameterNameDiscoverer
. The first uses the JDK8 JEP 118: Access to Parameter Names at Runtime . According to SPR-9643 , if it fails to determine the names of the parameters by the first strategy, Spring tries to use the "ASM-based debug symbol analysis".
-parameters
flag is -parameters
. I'm going to the Spring Boot settings of the IDEA project:Yes, it is really included ... And what if I collect and launch a project through Maven?
The result is the same!
I turn on the debug output in the Maven settings, compile the project and see:
[DEBUG] Goal: org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) ... <parameters default-value="false">true</parameters>
It seems that the maven-compiler-plugin
already configured in the spring-boot-starter-parent
, from which spring-boot
projects are inherited by default when generated via SPRING INITIALIZR . We go there and ( only for Spring Boot 2 ) for sure, the plugin is configured there:
<plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <parameters>true</parameters> </configuration> </plugin>
Finally, we can override the maven-compiler-plugin
configuration in our project, where we set this flag to false
. We check - the project started. And when you try to pull the method we get:
Unable to detect parameter names for query method ru.javaops.bootjava.restaurantvoting.repository.UserRepository.findByEmailIgnoreCase! Use @Param or compile with -parameters on JDK 8.
It means that:
TOTAL: The -parameters
flag in Spring Boot 2 is enabled by default, so if you inherit from spring-boot-starter-parent
, then the parameter names are defined in @Param
and @Param
, @RequestParam
, @PathVariable
no longer required. Less code, less error.
For Spring Boot 1.x, the compilation flag can be included forcibly, see above.
PS: in the study used JDK 8, JDK 11 and Spring Boot 2.1.1
PSS: if you have information when the second strategy is LocalVariableTableParameterNameDiscoverer
- plz share in the comments.
Thanks for attention!
Source: https://habr.com/ru/post/440214/