Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
Learn more about the adaptation to the latest Spring Boot 3.0.2 and Spring Data JPA 3.0.1.
Join the DZone community and get the full member experience.
Join For FreeLast year, I wrote two articles about JPA Criteria and Querydsl (see Introduction and Metamodel articles). Since the end of last year, there's been a new major release of Spring Boot 3. This release is based on Spring Framework 6 with several significant changes and issues which we should consider when upgrading.
The goal of this article is to highlight these changes when upgrading the sat-jpa project (SAT project). The technologies used here are:
- Spring Boot 3.0.2,
- Hibernate 6.1.6.Final
- Spring Data JPA 3.0.1 and
- Querydsl 5.0.0.
Spring Framework 6.0.4
Spring Framework 6 has many changes (see What's New in Spring Framework 6.x), the key changes are:
- Switch Java baseline to Java 17 (still the last Java LTS at the time of writing this article) — i.e., it's a minimum Java version we have to use.
- Switch Jakarta baseline to Jakarta EE 9+.
Besides that, there's just one minor change in Spring MVC used in SAT project.
Spring MVC 6.0.4
All methods in the ResponseEntityExceptionHandler
class changed their signatures. The status
argument was changed from HttpStatus
to HttpStatusCode
type (see the change in the CityExceptionHandler class).
HttpStatus in Spring MVC 5
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return buildResponse(BAD_REQUEST, exception);
}
HttpStatusCode in Spring MVC 6
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
HttpHeaders headers, HttpStatusCode status, WebRequest request) {
return buildResponse(BAD_REQUEST, exception);
}
Spring Boot 3.0.2
Spring Boot 3 has many changes (see Spring Boot 3.0 Release Notes); the most important are these ones:
- Java 17 (defined by the Spring Framework).
-
Bump up to Spring Framework 6 (see above).
-
Bump up to Hibernate 6 (see below).
- Migration from JavaEE to Jakarta EE dependencies — it's not important in our case as we don't rely on enterprise Java here, but it can affect many dependencies (e.g., servlet API, validation, persistence, JMS, etc.).
- Minor change in Spring MVC 6 in the
ResponseEntityExceptionHandler
class (see the next part).
Let's start with the simple Jakarta EE changes. So we can focus on persistence after that.
Jakarta EE Dependencies
The significant change is the migration from Java EE to Jakarta EE dependencies. Spring Framework 6 set the baseline as Jakarta EE 9 (see What's New in Spring Framework 6.x), but Spring Boot 3 already uses Jakarta EE 10 (see Spring Boot 3.0 Release Notes ) for many APIs (e.g., Servlet or JPA — to name some technologies).
As a consequence, all classes using another class from the jakarta
package has to switch to the same class from the javax
package instead (see, e.g., PostConstruct or Entity annotations).
Javax Imports
import javax.annotation.PostConstruct;
import javax.persistence.Entity;
Jakarta Imports
import jakarta.annotation.PostConstruct;
import jakarta.persistence.Entity;
Hibernate 6.1.6
Another major change in Spring Boot 3 is the upgrade from Hibernate 5.6 to Hibernate 6.1. The details about Hibernate 6.1 can be found here or inside the release notes.
Honestly, I didn't pay attention to this change until I had to fix one failing test due to a different result size (see the fix in the CountryRepositoryCustomTests
class). The implementation with the new Hibernate 6 returns fewer entities than before.
It's worth mentioning two changes here based on this investigation. Let's start with logging first.
Logging
The Hibernate loggers were changed from org.hibernate.type
to org.hibernate.orm.jdbc
(see this reference and this reference).
Note: all available configuration items can be found in org.hibernate.cfg.AvailableSettings
class.
Hibernate 5
There was a single logger for the bidden and extracted values.
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
Hibernate 6
Hibernate 6 changed the main package and split them into two different loggers in order to distinguish the operation -> what value should be logged.
logging.level.org.hibernate.orm.jdbc.bind=trace
logging.level.org.hibernate.orm.jdbc.extract=trace
Semantic Query Model
As the logging was fixed, it was confirmed that the Hibernate really extracts two records from DB:
2023-01-25T08:40:18.819+01:00 INFO 6192 --- [ main] c.g.a.s.j.c.CountryRepositoryCustomTests : Started CountryRepositoryCustomTests in 4.678 seconds (process running for 5.745)
Hibernate: select c2_0.id,c2_0.name from city c1_0 join country c2_0 on c2_0.id=c1_0.country_id where c1_0.name like ? escape '!' and c1_0.state like ? escape '!'
2023-01-25T08:40:19.221+01:00 TRACE 6192 --- [ main] org.hibernate.orm.jdbc.extract : extracted value ([1] : [BIGINT]) - [3]
2023-01-25T08:40:19.222+01:00 TRACE 6192 --- [ main] org.hibernate.orm.jdbc.extract : extracted value ([2] : [VARCHAR]) - [USA]
2023-01-25T08:40:19.240+01:00 TRACE 6192 --- [ main] org.hibernate.orm.jdbc.extract : extracted value ([1] : [BIGINT]) - [3]
2023-01-25T08:40:19.241+01:00 TRACE 6192 --- [ main] org.hibernate.orm.jdbc.extract : extracted value ([2] : [VARCHAR]) - [USA]
However, the test receives only a single entity from the Hibernate, as you can see in these debugging screenshots:
The changed behavior is caused by the new Semantic Query Model with Automatic deduplication (see Semantic Query Model part) introduced with the Hibernate 6 (see line 178 in org.hibernate.sql.results.spi.ListResultsConsumer
class). The Hibernate 6 returns the deduplicated result now.
Hibernate JPA Generator
The Hibernate Jpamodelgen maven dependency managed by Spring Boot Dependencies was moved from org.hibernate
to org.hibernate.orm
package. See:
Spring Boot 2.7.5
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>
Spring Boot 3.0.2
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>
Liquibase
To finish the core persistence dependencies, the Liquibase is upgraded from version 4.9.1 to 4.17.2. There's no significant difference except the JAX-B dependency usage. The dependency should use JAX-B from Jakarta instead of Javax (see the following reference).
Spring Boot 2.7.5
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
Spring Boot 3.0.2
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<exclusions>
<exclusion> <!-- due to SB 3.0 switch to Jakarta -->
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
Spring Data JPA 3.0.1
The Spring Boot 3.0.2 depends on Spring Data Release Train 2022.0.1 (see Spring Data 2022.0 - Turing Release Notes), where Spring Data JPA 3.0.1 is used with these key changes (see the release notes):
- Switch Java baseline to Java 17,
- Switch Jakarta baseline to Jakarta EE 10 and
- Bump up to Hibernate 6
Note: the previously used version (in our case) was the 2.7.5 version.
Querydsl 5.0.0
The Querydsl version was not changed, but it was impacted in a similar way as the Liquibase. The dependencies have to be used from Jakarta instead of Javax. Therefore, the Querydsl dependency has to use jakarta
classifier instead of the old jpa
classifier (see this reference).
Spring Boot 2.7.5
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jpa</classifier>
<scope>provided</scope>
</dependency>
Spring Boot 3.0.2
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>
Conclusion
This article has covered the upgrade to the latest Spring Boot 3.0.2 for JPA and Querydsl (at the time of writing this article). The article started with Spring Framework, and Spring Boot changes. Next, all changes to Hibernate and related technologies were covered. In the end, we mentioned the minor changes related to Spring Data 3.0.1 and Querydsl 5.0.0.
The complete source code demonstrated above is available in my GitHub repository.
Note: there is also a preceding article, Upgrade Guide To Spring Data Elasticsearch 5.0, dedicated to a similar upgrade of Elasticsearch with Spring Boot 3.
Opinions expressed by DZone contributors are their own.
Comments