Spring Boot 2 and JDK 8: Are you still using @Param, @RequestParam and @PathVariable annotations? Then the article is for you.
- Tutorial
Hello, Hablochitel!
While developing a Spring Boot 2 training project, I decided to experiment with @Param
Spring Data JPA queries, or rather the lack of them :
@Transactional(readOnly = true)
publicinterfaceUserRepositoryextendsJpaRepository<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 ).
By removing @Param
it, 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 decideddig 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 ofemail
):@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:
It can be seen 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".
- There is a lot of information on Java 8 Parameter Names on the Internet , compilation with a flag is necessary
-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 it is
maven-compiler-plugin
already configured inspring-boot-starter-parent
, from where projects are inherited by defaultspring-boot
when generating 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 redefine the configuration in our project
maven-compiler-plugin
, where we set this flag infalse
. 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:
- our reasoning is correct
- on the basis of the second ASM strategy, information could not be obtained (although I was running through Debug)
VERDICT: A flag -parameters
in Spring Boot 2 is enabled by default, so if you inherit from spring-boot-starter-parent
, the names of the parameters are defined in runtime and @Param
, @RequestParam
, @PathVariable
are 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 works LocalVariableTableParameterNameDiscoverer
- share plz in the comments.
Thanks for attention!