[JAVA] What I did in the migration from Spring Boot 1.5 series to 2.0 series

Introduction

Here has updated to SpringBoot 2.0.2. For projects that support Kotlin. Here, I will write about the work that occurred at that time.

--Before migration

--After migration

Also, since this project is running as a REST API server, we were able to avoid Thymeleaf migrations. I'm migrating a Spring MVC project separately, so I'll write about that separately.

I mainly referred to the following sites and articles.

First of all, upgrade of Spring Boot

We will modify pom.xml to upgrade the Spring Boot version.

Excerpt of changes

pom.xml


    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
 -    <version>1.5.8.RELEASE</version>
 +    <version>2.0.2.RELEASE</version>
      <relativePath />
    </parent>

・ ・ ・

      <dependency>
        <!-- https://docs.spring.io/spring-session/docs/current/reference/html5/guides/java-redis.html -->
        <groupId>org.springframework.session</groupId>
 -      <artifactId>spring-session</artifactId>
 +      <artifactId>spring-session-data-redis</artifactId>
      </dependency>

・ ・ ・

      <!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#jackson--json-support -->
 +    <dependency>
 +      <groupId>org.springframework.boot</groupId>
 +      <artifactId>spring-boot-starter-json</artifactId>
 +    </dependency>

・ ・ ・
      <!-Originally I was using Hikari PC, so I deleted the dependency-->
      <!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#configuring-a-datasource -->
 -    <dependency>
 -      <groupId>com.zaxxer</groupId>
 -      <artifactId>HikariCP</artifactId>
 -    </dependency>

・ ・ ・

      <!--Delete here after testing->
      <!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#before-you-start -->
 +    <dependency>
 +      <groupId>org.springframework.boot</groupId>
 +      <artifactId>spring-boot-properties-migrator</artifactId>
 +      <scope>runtime</scope>
 +    </dependency>

Avoid using WebMvcConfigurerAdapter of Application class

- public class XxxApplication extends WebMvcConfigurerAdapter {
+ public class XxxApplication implements WebMvcConfigurer{

Build-> Fix-> Rebuild

After setting pom.xml, build and repeat the modification.

Compile error in findOne of JPA Repository

First of all, the following error occurred.

Error:(41, 41) Kotlin: Type inference failed: fun <S : #{table name}!> findOne(p0: Example<S!>!): Optional<S!>!
cannot be applied to
(Long)
Error:(41, 49) Kotlin: Type mismatch: inferred type is Long but Example<(???..???)>! was expected

This is due to a change in the Spring Data CRUD method. https://spring.io/blog/2017/06/20/a-preview-on-spring-data-kay#improved-naming-for-crud-repository-methods

Change the error on the first line from findOne to findById. The error on the second line is get from ʻOptional because the return value of findByIdisJava Optional`.

Similarly, the arguments for save and delete have changed from those that pass List to those that pass ʻEntity, so call the saveAllanddeleteAllmethods instead. Of course, if only one record is affected, it is better to usesave and delete`, so consult with the time available for migration.

Unable to resolve ConfigureRedisAction

Error:(13, 53) java:Package org.springframework.session.data.redis.config does not exist

Although the order is out of order, ConfigureRedisAction could not be resolved by the following definition set to use ElastiCache. This is because it was cut out into a package called spring-session-data-redis.

https://docs.spring.io/spring-session/docs/current/reference/html5/guides/java-redis.html

	@Bean
	public static ConfigureRedisAction configureRedisAction() {
		return ConfigureRedisAction.NO_OP;
	}

See the spring-session part of pom.xml for the modifications. Also, I didn't add the lettuce dependency because spring-boot-starter-data-redis was originally included in the dependency.

Deprecation such as org.hibernate.validator.constraints.NotBlank

In line with this, we have replaced it with javax.validation.constraints.NotEmpty and so on.

Error in jdbc autoconfigure

Error:(7, 51) java:Can't find symbol
symbol:Class DataSourceBuilder
place:Package org.springframework.boot.autoconfigure.jdbc

This seemed to happen because I was working with multiple data sources.

application.properties


hoge.datasource.driver-class-name        = com.mysql.jdbc.Driver
hoge.datasource.url                    = jdbc:mysql://127.0.0.1:33306/hoge
hoge.datasource.username                = hoge
hoge.datasource.password                = fuga
#Others omitted
#Above x for multiple data sources

Originally it was set as follows

HogeDatabaseConfig.java


 @Configuration
 @EnableTransactionManagement
 @EnableJpaRepositories(entityManagerFactoryRef = "hogeEntityManagerFactory", transactionManagerRef = "hogeTransactionManager", basePackages = {
 		"jp.xxx.data.hoge.repository" })
 public class HogeDatabaseConfig extends AbstractDatabaseConfig {
 
 	@Bean(name = "hogeDataSource")
	@ConfigurationProperties(prefix = "hoge.datasource")
 	public DataSource hogeDataSource() {
		return DataSourceBuilder.create().build();
 	}
 
 	@Bean(name = "hogeEntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean hogeEntityManagerFactory(EntityManagerFactoryBuilder builder,
		return builder.dataSource(hogeDataSource)
		        .packages("jp.xxx.data.hoge.entity").persistenceUnit("hoge")
				.properties(buildProperties()).build();
 	}
 
 	@Bean(name = "hogeTransactionManager")
 	public PlatformTransactionManager hogeTransactionManager(
 			@Qualifier("hogeEntityManagerFactory") EntityManagerFactory hogeEntityManagerFactory) {
 		return new JpaTransactionManager(hogeEntityManagerFactory);
 	}
 }

However, I fixed it as follows. I think there is a better way to do this part.

HogeDatabaseConfig.java


 @Configuration
 @ConfigurationProperties(prefix = "hoge.datasource")
 @EnableTransactionManagement
 @EnableJpaRepositories(entityManagerFactoryRef = "hogeEntityManagerFactory", transactionManagerRef = "hogeTransactionManager", basePackages = {
 		"jp.xxx.data.hoge.repository" })
 public class HogeDatabaseConfig extends AbstractDatabaseConfig {
 
	@Autowired
	private Environment env;

 	@Bean(name = "hogeDataSource")
 	public DataSource hogeDataSource() {
		final DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName(env.getProperty("hoge.datasource.driver-class-name"));
		dataSource.setUrl(env.getProperty("hoge.datasource.url"));
		dataSource.setUsername(env.getProperty("hoge.datasource.username"));
		dataSource.setPassword(env.getProperty("hoge.datasource.password"));
		return dataSource;
 	}
 
 	@Bean(name = "hogeEntityManagerFactory")
	public EntityManagerFactory hogeEntityManagerFactory(
 			@Qualifier("hogeDataSource") DataSource hogeDataSource) {
		final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
		factoryBean.setDataSource(hogeDataSource);
		factoryBean.setPersistenceUnitName("hoge");
		factoryBean.setPackagesToScan("jp.xxx.data.hoge.entity");
		factoryBean.setJpaPropertyMap(buildProperties());
		factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
		factoryBean.afterPropertiesSet();
		return factoryBean.getNativeEntityManagerFactory();
 	}
 
 	@Bean(name = "hogeTransactionManager")
 	public PlatformTransactionManager hogeTransactionManager(
 			@Qualifier("hogeEntityManagerFactory") EntityManagerFactory hogeEntityManagerFactory) {
 		return new JpaTransactionManager(hogeEntityManagerFactory);
 	}
 }

Tomcat starts here

Although Tomcat started, the following error log was flowing.

java.lang.ClassCastException: jp.xxx.service.HogeService$Fuga cannot be cast to org.springframework.core.io.support.ResourceRegion
	at org.springframework.http.converter.ResourceRegionHttpMessageConverter.writeResourceRegionCollection(ResourceRegionHttpMessageConverter.java:182)
	at org.springframework.http.converter.ResourceRegionHttpMessageConverter.writeInternal(ResourceRegionHttpMessageConverter.java:139)
	at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:272)
	at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
	at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)

It seems that it is occurring because PostgreSQL is also in the data source.

This was avoided by creating and placing hibernate.properties.

hibernate.properties


hibernate.jdbc.lob.non_contextual_creation = true

Up to this point, no error has occurred by the time of startup.

Sudden ClassCastException

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method jp.xxx.controller.HogeController.createFuga, parameter form
	at jp.xxx.controller.HogeController.createCampaign(HogeController.kt)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

The argument type of form was converted from HogeForm to the nullable type of HogeForm?.

Date type conversion error

Failed to convert value of type 'java.lang.String[]' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2018-06-11T00:00:00'; nested exception is java.lang.IllegalArgumentException

I was a little addicted to this part, but I didn't understand the cause, but it worked by changing the object that received the data from the Kotlin Data class to a normal Kotlin class.

If it is PUT, the value of form will not be passed

I didn't have time to investigate the cause, so I rewrote the PUT part to POST ...

If the return type of REST Controller is List <Object>, it cannot be converted to JSON and an error will occur.

I forgot to take the error log.

Originally such an implementation In the first place, do not return with List ...

    @GetMapping("/hoge")
    public List<Object> hoge() {
        return hogeService.hoges();
    }

Since the return type was quite complicated, I made it JSON on the Controller side and returned it.

    @GetMapping("/hoge")
    public String hoge() throws JsonProcessingException {
        final List<Object> hoges = hogeService.hoges();
        final ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(hoges);
    }

With the above, the migration is completed and the migration is completed.

Recommended Posts