After upgrading from Spring Boot 2.2.5 to 2.3.0.M3 I found javax.persistence-api
on the classpath, together with jakarta.persistence-api
although only jakarta.persistence-api
should be on the classpath.
It is possible that this behavior is a result of https://github.com/spring-projects/spring-boot/issues/19609.
This can be verified with the following minimal example.
Given the following build.gradle.kts
:
plugins {
`java-library`
}
repositories {
mavenCentral()
maven("https://repo.spring.io/milestone")
}
val springBootVersion = "2.3.0.M3"
dependencies {
api(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
}
Running: ./gradlew dependencies --configuration runtimeClasspath
shows that the classpath contains both javax.persistence-api:2.2
and jakarta.persistence-api:2.2.3
.
runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:2.3.0.M3
| +--- jakarta.persistence:jakarta.persistence-api:2.2.3 (c)
| +--- javax.persistence:javax.persistence-api:2.2 (c)
\--- org.springframework.boot:spring-boot-starter-data-jpa -> 2.3.0.M3
+--- jakarta.persistence:jakarta.persistence-api -> 2.2.3
+--- org.hibernate:hibernate-core -> 5.4.12.Final
| +--- javax.persistence:javax.persistence-api:2.2
Whereas with Spring Boot 2.2.5.RELEASE
the classpath only contains jakarta.persistence-api:2.2.3
.
runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:2.2.5.RELEASE
| +--- jakarta.persistence:jakarta.persistence-api:2.2.3 (c)
\--- org.springframework.boot:spring-boot-starter-data-jpa -> 2.2.5.RELEASE
+--- jakarta.persistence:jakarta.persistence-api:2.2.3
Bonus:
It is not possible to exclude javax.persistence-api
manually, the result is still the same as without exclude.
dependencies {
api(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
implementation("org.springframework.boot:spring-boot-starter-data-jpa") {
exclude(group = "javax.persistence", module = "javax.persistence-api")
}
}
Comment From: wilkinsona
Thanks for the report.
I've confirmed the unwanted presence of javax.persistence-api
with 2.3.0.M3. It isn't present if you use 2.3.0.M2 (where we didn't publish the module metadata) so that's a pointer towards the metadata being at least part of the cause.
The problem's particularly nasty as our build polices the presence of duplicate classes and resources in all of the starters. No duplicates appear when we're building spring-boot-starter-data-jpa
as it appears that in that context its exclusions are effective.
I've also reproduced the problem where it is impossible to exclude javax.persistence-api
. Interestingly, this only happens when using Gradle's platform
to apply the spring-boot-dependencies
bom. If you use the dependency management plugin, this part of the problem does not occur.
It looks like we're hitting a bug in Gradle so I've reached out to them for some help. We'll keep this issue open as we may need to stop publishing the metadata and we can use it to make that change if necessary.
Comment From: ljacomet
Hello,
There is an underlying bug in Gradle indeed, which will be fixed for Gradle 6.3, see gradle/gradle#12536. A workaround for the issue is to use a configuration level exclude:
configurations.runtimeClasspath {
exclude(module = "javax.persistence-api")
}
Comment From: wilkinsona
For M4, we're going to try bumping the supported version of Gradle 6.x to 6.3. We'll continue to support Gradle 5.6 which isn't affected by this problem. We may adjust course a little depending on the feedback we get on cutting off support for 6.0, 6.1, and 6.2.
Comment From: jjohannes
We just released Gradle 6.3 RC2 that fixes the underlying issue.
Comment From: dawi
I can confirm that with Gradle 6.3 RC2 it works as expected (as with Spring Boot 2.2.5).
@wilkinsona @ljacomet I have one related question regarding transitive dependency exclusions and I am not sure if I should file another issue, if it is expected behavior or if it is being addressed already. Maybe it is related to https://github.com/spring-projects/spring-boot/issues/13957 (which is still open) and https://github.com/gradle/gradle/issues/1473 (which is closed).
With the following dependency declaration:
dependencies {
api("org.springframework.boot:spring-boot-starter-data-jpa")
}
I get a hibernate-core dependency where all exclusions defined by spring are effective.
If I now add hibernate-jcache
as dependency, the exclusions defined by spring are not effective anymore. I have to exclude hibernate-core
manually to not get unwanted transitive dependencies like this:
dependencies {
api("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.hibernate:hibernate-jcache") {
exclude(module = "hibernate-core")
}
}
To be sure not to accidentally have unwanted dependencies, I added several constraints like this:
api("javax.persistence:javax.persistence-api") {
version {
rejectAll()
because("We should use jakarta.persistence-api instead.")
}
}
If I hadn't had this constraints, I might not have noticed this issue in the first place.
Comment From: snicoll
FTR this is no longer blocked as 6.3 is out.