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 @ParamSpring 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 @Paramit, 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 ( mailinstead 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:




It can be seen that 2 strategies are used: StandardReflectionParameterNameDiscovererand 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-pluginalready configured in spring-boot-starter-parent, from where projects are inherited by default spring-bootwhen 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 in 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:


  1. our reasoning is correct
  2. on the basis of the second ASM strategy, information could not be obtained (although I was running through Debug)

VERDICT: A flag -parametersin 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, @PathVariableare 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!


Also popular now: