Migration Guide from Spring Boot 1.5 to 2.2.x

Norbert Nowak
RSQ Technologies
5 min readApr 16, 2020

--

Hello!

I am Norbert and I work at RSQ Technologies as a software developer. One of our main products is RSQ Health — a system that supports clinics, hospitals and doctors in their everyday work with online documentation, timetables and patient database. We also work on AI and hardware but these are not the topic of this article.

Let’s start with some project background.

Our core app is a monolith. There are also some helping microservices around it.

As you might guess — Yes, this article is about that core.

Spring Boot 1.5.6 was the latest version when we have started writing our monolith in 2017. It’s been a long time since then — numerous new versions of Spring Boot were released in the meantime.

Months were passing and we were adding new features, bug fixes etc… Nobody cared about the outdated Spring Boot until the moment I estimated that some tasks related to Hibernate update would take me (approximately) 1 hour. I found out it was related to an outdated dependency. But… digging deeper I noticed that this is not a problem of the dependency but the whole outdated Spring Boot. So I decided to start a conversation about the update. At our company, at one of our retrospective/planning weekly meetings I convinced my teammates that it is the time and we have to do that! For us and for the greater good! (I said it can take more than 1 day)

Finally the whole migration process took me 2 weeks, with pull requests and fixes included.

I would like to share my experience with you.

Why?

Let me explain why you also should check/update dependencies and have them up-to-date.

  1. “It’s time to say goodbye”
    Official Spring blog post from 09–06–2019 says it’s the end of 1.x version life.
  2. Compatibility
    We have several other services working on Spring Boot 2.x. For communication we are using Feign client which had compatibility issues between such different versions.
  3. Performance
    Jenkins builds used to take about 1.5h and after update it’s 0.5h.
  4. New features
    Latest features and possibility of update to Java 11.
  5. Happy teammates

Let’s start

After one month I looked back on git history and all of commits that were made. Fortunately, during migration I kept idea of this article in mind.

Firstly, I would like to list how versions of some project dependencies have changed. In the next section, I will describe to you some steps.

Steps

Below I present selected cases on which I’ve spent some time. Enjoy, and I hope it will end up helpful.

  • Imports changes
    There are many many imports that have changed with dependencies. I will not list you all the dependencies from our project. You can easily find compatibility matrix for each. For example Spring Cloud. Your IDE will show you all import/dependency problems during compilation.
  • Application properties
    Properties migrator dependency is really helpful during application properties migration (e.g application.yml). It will find and make all temporary replacements for you. Furthermore, it will print for you a warning about every change.
    If you would like to do it on your own — feel free! Here is the list of all properties used in Spring.
  • Renaming JPA functions
    The following CrudRepository methods have been renamed:
save → saveAll
delete → deleteAll
  • CrudRepository findOne
    Previous point only tells you about repository method name replacement. However, there is another change that requires more time. Repository function findOne has been replaced by findById and now it returns an Optional. It could be too messy to replace it in the whole project so I’ve created an extension below:

You can also use orElseThrow and throw an exception instead of returning null.

  • HttpServletRequestMapper is using attributes instead of URI.
    We are using request mapper in the versioning process to lookup for versions of a given endpoint.
  • Health indicators on startup
    Honestly I didn’t notice it in Spring Boot 1.5.4 but according to documentation it was already there. 😕
    If you are using spring. mail MailHealthIndicator will check connection on startup. You can disable it in properties by setting: management.health.mail.enabled=false
  • Abstract class need open val/var
    Classes that extend or override regular params of an abstract class would not work. Abstract class params need to be open.
  • LocalDate/Time in entities requires columnDefinition
    Having LocalDate/Time in your entity can be problematic for schema creator. I had to add columnDefinitions to every entity column of type as below:
java.time.LocalDate → @Column(columnDefinition=DATE)
java.time.LocalTime → @Column(columnDefinition=TIME)
  • Default Charset
    I’ve had problems with JSON encoding in integration tests. The response had incorrectly encoded characters. Not sure what was the cause but I found a solution. I’ve added default charset to our JacksonConfig.
  • Page object JSON format change
    After migration Page object consumed as JSON structure has changed. I’ve figured it out when one of tests has failed. After some research, I’ve added this construction in our CustomPageImpl:

Helpful links: StackOverflow, blog post.

  • Bean overriding
    In case of having multiple beans of the same name based on active profile, adding allow-bean-definition-overriding: true helped
  • Gradle BootRepackage renamed
    According to this article, bootRepackage has been replaced with bootJarand bootWar.
  • Flyway default Schema History Table renamed
    Flyway version have changed from 3.x → 6.x.
    So you will need to rename default table name in application properties. Flyway 6.x is uses flyway_schema_history as default table. It’s described in detail on StackOverflow.
flyway:
table: "schema_version"

Another solution is to rename table directly in database.

  • Integration and unit test
    Having about 2k tests written with JUnit 4 and migrating them to JUnit 5 seems to be time-consuming. JUnit Vintage dependency turned out to be helpful. It provides engine for running JUnit 4 tests.
    If you would like to migrate to Junit 5, JUnit docs or this article could be helpful.
  • Dependencies cleanup
    Running ./gradlew dependencies shows dependency tree and possible updates. After executing this task I’ve found some dependencies that we imported separately before migration which are now included in another dependency or just duplicated.
    For example:
    After update we had two hamcrest dependencies.
    And this is gradle task execution result:

So I’ve simply removed the older one.

Summary

Icons made by Freepik from https://www.flaticon.com/

So here we are. Project builds, Jenkins is green, I’m alive, teammates are happy, our project is as fresh as when it was initialized 3 years ago. I would like to share some final insights:

  • Check out official Spring Boot migration article and use properties migrator that will speed up first steps.
  • Keep your git clean. Work on separate branch. Merge master to avoid conflicts. Describe changes in commits. I think that without it I wouldn’t be able to write this article today.
  • If you have other services depending on each other be careful about dependency compatibility e.g. Spring Cloud.
  • Keep dependencies up to date. ./gradlew dependencies will help you find updates.

Hopefully you will spend less time than I did.

--

--