Spring Boot 2 to 3 Upgrade: Procedure, Challenges, and Solutions
Spring Boot 2 to 3 Upgrade: Procedure, Challenges, and Solutions
Introduction
Hello. I am Takehana from the Payment Platform Team / Common Service Development Group [1][2][3][4] / Platform Development Division.
This article covers the latest Spring Boot update which we use for payment platform APIs and batches.
Challenges to Solve and Goals I Wanted to Achieve
- I am using Spring Boot 2, and I want to upgrade to 3 in consideration of the support period and other factors.
- The version of the library used was also upgraded
Library | Before migration (2) | After migration (3) |
---|---|---|
Java | 17 | No changes |
MySQL | 5.7 | 8.0 |
Spring Boot | 2.5.12 | 3.1.0 |
Spring Boot Security | 2.5.12 | 3.1.0 |
Spring Boot Data JPA | 2.5.12 | 3.1.0 |
hibernate Types | 2.21.1 | 3.5.0 |
MyBatis Spring Boot | 2.2.0 | 3.0.2 |
Spring Batch | 4.3 | 5.0 |
Spring Boot Batch | 2.5.2 | 3.0.11 |
Spring Boot Cloud AWS | 2.4.4 | 3.0.1 |
Trial and Error and Measures Taken
Method of Application
We first updated and deprecated libraries that have little impact with existing code while referring to the official migration guide.
After that, we updated to 3.1.0 and continued fixing, building, testing, and adjusting.
Spring Boot 3.1 Release Notes
Spring Boot 3.0 Migration Guide
Spring Batch 5.0 Migration Guide
javax
→ Jakarta
We changed packages from javax
, which affected many files, to Jakarta
.
The name after the package root did not change, so we replaced it mechanically.
Around DB access
MySQL-Connector-Java
I changed to mysql-connector-j because it was migrated.
Maven Repository
MySQLDialect
Using org.hibernate.dialect.MySQLDialect
allows it to absorb MySQL version differences.
Hibernate-Types
The method of setting the Json type used with JPA Entity has changed with the upgrade.
Change ID generate to IDENTITY
The automatic numbering method has changed in Spring DATA JPA, and it now requires the table XXX_seq
when using AUTO.
Since our system used MySQL's Auto Increment, we decided not to use the numbering feature with JPA.
Spring Batch
Modifying the Meta Table
The structure of the Spring Batch management table changed.
The existing table was changed using the migration guide as a reference and using the following.
/org/springframework/batch/core/migration/5.0/migration-mysql.sql
However, just executing the ALTER TABLE caused an error during operation due to existing data, so we decided to restore the data to its initial state after confirming that it would not affect future operation.
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: LONG
at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao$2.processRow(JdbcJobExecutionDao.java:468)
...
(The PARAMETER_TYPE of BATCH_JOB_EXECUTION_PARAMS contained a LONG value)
The data was restored to its initial state with the following SQL.
TRUNCATE TABLE BATCH_STEP_EXECUTION_CONTEXT;
TRUNCATE TABLE BATCH_STEP_EXECUTION_SEQ;
TRUNCATE TABLE BATCH_JOB_SEQ;
TRUNCATE TABLE BATCH_JOB_EXECUTION_SEQ;
TRUNCATE TABLE BATCH_JOB_EXECUTION_PARAMS;
TRUNCATE TABLE BATCH_JOB_EXECUTION_CONTEXT;
SET foreign_key_checks = 0;
TRUNCATE TABLE BATCH_JOB_EXECUTION;
TRUNCATE TABLE BATCH_JOB_INSTANCE;
TRUNCATE TABLE BATCH_STEP_EXECUTION;
SET foreign_key_checks = 1;
INSERT INTO BATCH_STEP_EXECUTION_SEQ VALUES(0, '0');
INSERT INTO BATCH_JOB_EXECUTION_SEQ VALUES(0, '0');
INSERT INTO BATCH_JOB_SEQ values(0, '0');
BasicBatchConfigurer can no longer be used
The method of use was changed to DefaultBatchConfiguration.
StepBuilderFactory and JobBuilderFactory were deprecated
JobRepository and TransactionManager are now passed with new StepBuilder()
.
The argument type of ItemWriter changed
The write process was fixed after List changed to org.springframework.batch.item.Chunk
.
Before correction | After correction |
---|---|
<pre>ItemWriter<Dto> write() { return items -> { ... items.stream() .flatMap(dto -> dto.getDatas().stream()) .forEach(repository::update); ...</pre> |
<pre>ItemWriter<Dto> write() { return items -> { ... items.getItems().stream() .flatMap(dto -> dto.getDatas().stream()) .forEach(repository::update); ...</pre> |
The behavior of @EnableBatchProcessing changed
When checking operations, the process was skipped with chunk model batches.
The behavior of @EnableBatchProcessing
changed.
Spring Cloud AWS
Library changes
This system uses many AWS services, and Spring Cloud AWS was used to link them.
With the update, io.awspring.cloud:spring-cloud-starter-aws
was changed to io.awspring.cloud:spring-cloud-aws-starter
(confusing), and com.amazonaws:aws-java-sdk
was replaced with software.amazon.awssdk
and fixed so it would operate.
Spring Cloud AWS
SES
Regarding SES, because AmazonSimpleEmailService
is no longer usable, we switched to JavaMailSender for the implementation.
The JavaMailSender used is built with SES Auto Configuration and used with DI.
SQS
Objects for requests such as sending to SQS are made with the Builder pattern, so we fixed them accordingly.
In addition, @NotificationMessage used in SQSListener is gone, so we created SqsListenerConfigurer and prepared MessageConverter.
@Bean
public SqsListenerConfigurer configurer(ObjectMapper objectMapper) {
return registrar ->
registrar.manageMessageConverters(
list ->
list.addAll(
0,
List.of(
new SQSEventModelMessageConverter(
objectMapper, ReciveEventModel.class),
...
}
@RequiredArgsConstructor
private static class SQSEventModelMessageConverter implements MessageConverter {
private static final String SQS_EVENT_FILED_MESSAGE = "Message";
private final ObjectMapper objectMapper;
private final Class<?> modelClass;
@Override
public Object fromMessage(Message<?> message, Class<?> targetClass) {
if (modelClass != targetClass) {
return null;
}
try {
val payload =
objectMapper
.readTree(message.getPayload().toString())
.get(SQS_EVENT_FILED_MESSAGE)
.asText();
return objectMapper.readValue(payload, targetClass);
} catch (IOException ex) {
throw new MessageConversionException(
message, " Could not read JSON: " + ex.getMessage(), ex);
}
...
}
S3
For uploads to S3, TransferManager
was changed to S3TransferManager
, and the implementation of issuing signed URLs needed to be fixed.
SNS
The sns:CreateTopic privilege was required with DefaultTopicArnResolver
for sending SNS.
We made it so TopicsListingTopicArnResolver can be used now, and CreateTopic permission is no longer needed.
@ConditionalOnProperty("spring.cloud.aws.sns.enabled")
@Configuration
public class SNSConfig {
@Bean
public TopicArnResolver topicArnResolver(SnsClient snsClient) {
return new TopicsListingTopicArnResolver(snsClient);
}
}
Around the API
WebSecurityConfigurerAdapter cannot be referenced
We switched to a method that uses SecurityFilterChain referring to links.
spring-security
Making stricter URL paths
Trailing Slash paths are now strictly differentiated.
Since this system was linked with another internal system, we added a path to @RequestMapping
, adjusted it with the peer system, and removed the added path.
Before addition | After addition |
---|---|
<pre>... @RequestMapping( method = RequestMethod.GET, value = {"/api/payments/{id}"}, ... </pre> |
<pre>... @RequestMapping( method = RequestMethod.GET, value = {"/api/payments/{id}", "/api/payments/{id}/"}, ... </pre> |
Renaming properties (application.yml)
Properties such as jpa, redis, and spring-cloud-AWS were renamed.
We adjusted them according to the official information.
Deployment
There is a 404 with ECS deployment
We reached the point where we could deploy to ECS and confirmed the launch in the application log, but when I accessed the API, there was a 404.
I checked and saw that the Health Check failed the ECS deployment.
With the help of our cloud platform engineers, we discovered that the AWS-opentelemetry-agent
version used for telemetry data collection was outdated.
By changing to a version of jar 1.23.0 or later, we can successfully deploy and check API communication.
OpenTelemetryJava provided by AWS
Results, additional knowledge and application proposals, next attempts, etc.
There are places that did not have the general implementation pattern of Spring Boot which meet various requirements.
The structure did not allow us to migrate easily with just migration guide sometimes.
We managed to release it after repeated trial and error.
I would like to thank the team for their continued work and reviews.
We will continue to address the following remaining issues while also taking advantage of the features that were improved in Spring Boot 3.
Swagger-UI
We put off upgrading this.
Since spring-fox is not yet compatible with Spring Boot 3, we are considering changing to springdoc-openapi.
Spring Batch + MySQL 8.0 + DbUnit
It results in an error if it meets some conditions.
It seems that it is related to Spring Batch transaction management (meta table operation), and we are looking into how to fix it.
Summary of the Lessons of This Article
- We were able to upgrade Spring Boot by repeating the build and test while referring to the migration guide.
- This update had a wide range of effects, but by preparing tests, we found out what we had to fix, and we were able to address them efficiently.
- We also found problems by running operations such as trying to change @EnableBatchProcessing, so we also had to check by running operations.
- Regarding JavaEE, with the change to Jakarta, we had to update the Spring Boot library and others.
- Security is stronger (trailing slash rules are stricter, AuthFilter can be used with Ignore Path, etc.).
- There were differences in the updates for each dependent library, and Spring Cloud AWS was especially different.
- We may have needed to make less changes if we had upgraded libraries more frequently.
Thank you for reading This article.
I hope that it will be useful for those who are also considering upgrading.
Posted by a member of the Common Services Development Group
[Domain-Driven Design (DDD) is incorporated into a payment platform with a view to global expansion ] ↩︎Posted by a member of the Common Services Development Group
[New Employees Develop a New System with Remote Mob Programming] ↩︎Posted by a member of the Common Services Development Group
[Improving Deployment Traceability with JIRA and GitHub Actions] ↩︎Posted by a member of the Common Services Development Group
[Building a Development Environment with VSCode Dev Container] ↩︎
関連記事 | Related Posts
We are hiring!
【バックエンドエンジニア】my route開発G/東京
my route開発グループについてmy route開発グループは、my routeに関わる開発・運用に取り組んでいます。my routeの概要 my routeは、移動需要を創出するために「魅力ある地域情報の発信」、「最適な移動手段の提案」、「交通機関や施設利用のスムーズな予約・決済」をワンストップで提供する、スマートフォン向けマルチモーダルモビリティサービスです。
【プロダクト開発バックエンドエンジニア(リーダークラス)】共通サービス開発G/東京・大阪
共通サービス開発グループについてWebサービスやモバイルアプリの開発において、必要となる共通機能=会員プラットフォームや決済プラットフォームなどの企画・開発を手がけるグループです。KINTOの名前が付くサービスやKINTOに関わりのあるサービスを同一のユーザーアカウントに対して提供し、より良いユーザー体験を実現できるよう、様々な共通機能や顧客基盤を構築していくことを目的としています。