spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.57k stars 40.55k forks source link

spring-boot-starter-data-jpa pulls in multiple dependencies providing package 'javax.persistence' #21220

Closed holgerstolzenberg closed 4 years ago

holgerstolzenberg commented 4 years ago

I am using:

My build is using the Spring Boot and the Spring Dependency management plugin:

buildscript {
  repositories {
    maven {
      url "http://repo.spring.io/milestone"
    }
  }
  dependencies {
    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
  }
}

plugins {
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}

apply plugin: "org.springframework.boot"

dependencyManagement {
  imports {
    mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
  }
}

dependencies {
   implementation('org.springframework.boot:spring-boot-starter-web') {
    exclude module: "spring-boot-starter-tomcat"
  }
  implementation 'org.springframework.boot:spring-boot-starter-jetty'
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

  // …
}

On applying Querydsl to the project I've noticed that two versions of javax.persistence.Entity are pulled into the classpath:

This results in duplicated classes on the classpath and might cause hard to track down problems.

Please see attached screenshots.

Is that the intended behaviour? Am I missing something here?

wilkinsona commented 4 years ago

Thanks for the report. This isn't intended behaviour. We've seen this problem before (https://github.com/spring-projects/spring-boot/issues/20532) but it should have been fixed by Gradle 6.3. Can you please double-check that you are using that version of Gradle?

holgerstolzenberg commented 4 years ago
idea-classpath gradle-dependencies
holgerstolzenberg commented 4 years ago

Here is the version output from the CLI:

❯ ./gradlew --version

------------------------------------------------------------
Gradle 6.3
------------------------------------------------------------

Build time:   2020-03-24 19:52:07 UTC
Revision:     bacd40b727b0130eeac8855ae3f9fd9a0b207c60

Kotlin:       1.3.70
Groovy:       2.5.10
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          14.0.1 (AdoptOpenJDK 14.0.1+7)
OS:           Mac OS X 10.15.4 x86_64
holgerstolzenberg commented 4 years ago

As there are other dependencies involved, I could provide the full build script, but it looks like javax.persistence:javax.persistence-api does not get excluded from Hibernate.

wilkinsona commented 4 years ago

On second reading, I suspect "on applying Querydsl to the project" is key to this. I can't see that dependency in the build.gradle script your provided above.

My suspicion is that QuerryDSL has a transitive dependency on hibernate-core where there is no exclusion of the javax.persistence:javax.persistence-api dependency. For an exclusion to be effective in Gradle, it has to be applied on all routes to the unwanted dependency. We may be able to add an exclusion in our dependency management for QueryDSL, but we'll need confirmation of my assumption first. Can you please provide a complete and minimal build.gradle script that reproduces the behaviour you have described?

holgerstolzenberg commented 4 years ago

Commenting out the querydsl dependencies does not make a difference in my build. I have stripped down the build script:

buildscript {
  repositories {
    maven {
      url "http://repo.spring.io/milestone"
    }
  }
  dependencies {
    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
  }
}

plugins {
  id 'org.gradle.wrapper'
  id 'org.gradle.java'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  // TODO scheduled RC 24.04. id 'org.springframework.boot'
  id 'com.intershop.gradle.buildinfo' version '6.0.0'
}

apply plugin: "org.springframework.boot"

wrapper {
  gradleVersion = '6.3'
}

repositories {
  maven {
    url "http://repo.spring.io/milestone"
  }
}

dependencyManagement {
  imports {
    mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
  }
}

dependencies {
  annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
  annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
  annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
  annotationProcessor 'javax.annotation:javax.annotation-api'
  annotationProcessor 'org.projectlombok:lombok'
  annotationProcessor group: 'com.querydsl', name: 'querydsl-apt', classifier: 'jpa'

  compileOnly 'org.springframework.boot:spring-boot-devtools'
  compileOnly "org.projectlombok:lombok"

  implementation('org.springframework.boot:spring-boot-starter-web') {
    exclude module: "spring-boot-starter-tomcat"
  }
  implementation 'org.springframework.boot:spring-boot-starter-jetty'
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  implementation 'org.springframework.boot:spring-boot-starter-security'
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  implementation "org.zalando:problem-spring-web-starter:${zalandoProblemVersion}"
  implementation "org.springdoc:springdoc-openapi-ui:${openApiUiVersion}"
  implementation "com.google.guava:guava:${guavaVersion}"
  implementation 'org.flywaydb:flyway-core'
  implementation 'org.postgresql:postgresql'
  implementation 'org.hibernate:hibernate-java8'
  implementation 'com.querydsl:querydsl-jpa'
  implementation "org.mapstruct:mapstruct:${mapstructVersion}"

  testAnnotationProcessor "org.projectlombok:lombok"
  testCompileOnly "org.projectlombok:lombok"
  testImplementation "org.springframework.cloud:spring-cloud-starter"
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'org.springframework.security:spring-security-test'
  testImplementation "io.rest-assured:rest-assured:${restAssuredVersion}"
  testImplementation "io.rest-assured:json-path:${restAssuredVersion}"
  testImplementation "io.rest-assured:xml-path:${restAssuredVersion}"
  testImplementation 'org.assertj:assertj-core'
  testImplementation "com.playtika.testcontainers:embedded-keycloak:${testcontainersVersion}"
  testImplementation "com.playtika.testcontainers:embedded-postgresql:${testcontainersVersion}"
}

compileJava {
  // so that spring-boot config processor picks up meta data
  inputs.files(processResources)
}

bootJar {
  excludeDevtools = true
}

Let me know if you need the full one.

guavaVersion=28.2-jre
openApiUiVersion=1.2.33
mapstructVersion=1.3.1.Final
restAssuredVersion=4.2.0
springBootVersion=2.3.0.M4
springCloudVersion=Hoxton.SR3
testcontainersVersion=1.48
zalandoProblemVersion=0.25.2
wilkinsona commented 4 years ago

Thanks. That's not really minimal, but I think I've managed to identify the problem. It's caused by your org.hibernate:hibernate-java8 dependency. It introduces another route to org.hibernate:hibernate-core that does not exclude javax.persistence:persistance-api. You can avoid the problem by adding an exclusion:

implementation('org.hibernate:hibernate-java8') {
    exclude group: 'javax.persistence', module: 'javax.persistence-api'
}

I also had to correct the milestone repository URLs (to use HTTPS) and add mavenCentral(). This left things looking like the following:

buildscript {
  repositories {
    maven {
      url "https://repo.spring.io/milestone"
    }
  }
  dependencies {
    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
  }
}

plugins {
  id 'org.gradle.wrapper'
  id 'org.gradle.java'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  // TODO scheduled RC 24.04. id 'org.springframework.boot'
  id 'com.intershop.gradle.buildinfo' version '6.0.0'
}

apply plugin: "org.springframework.boot"

wrapper {
  gradleVersion = '6.3'
}

repositories {
  maven {
    url "https://repo.spring.io/milestone"
    mavenCentral()
  }
}

dependencyManagement {
  imports {
    mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
  }
}

dependencies {
  annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
  annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
  annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
  annotationProcessor 'javax.annotation:javax.annotation-api'
  annotationProcessor 'org.projectlombok:lombok'
  annotationProcessor group: 'com.querydsl', name: 'querydsl-apt', classifier: 'jpa'

  compileOnly 'org.springframework.boot:spring-boot-devtools'
  compileOnly "org.projectlombok:lombok"

  implementation('org.springframework.boot:spring-boot-starter-web') {
    exclude module: "spring-boot-starter-tomcat"
  }
  implementation 'org.springframework.boot:spring-boot-starter-jetty'
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  implementation 'org.springframework.boot:spring-boot-starter-security'
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  implementation "org.zalando:problem-spring-web-starter:${zalandoProblemVersion}"
  implementation "org.springdoc:springdoc-openapi-ui:${openApiUiVersion}"
  implementation "com.google.guava:guava:${guavaVersion}"
  implementation 'org.flywaydb:flyway-core'
  implementation 'org.postgresql:postgresql'
  implementation('org.hibernate:hibernate-java8') {
    exclude group: 'javax.persistence', module: 'javax.persistence-api'
  }
  implementation 'com.querydsl:querydsl-jpa'
  implementation "org.mapstruct:mapstruct:${mapstructVersion}"

  testAnnotationProcessor "org.projectlombok:lombok"
  testCompileOnly "org.projectlombok:lombok"
  testImplementation "org.springframework.cloud:spring-cloud-starter"
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'org.springframework.security:spring-security-test'
  testImplementation "io.rest-assured:rest-assured:${restAssuredVersion}"
  testImplementation "io.rest-assured:json-path:${restAssuredVersion}"
  testImplementation "io.rest-assured:xml-path:${restAssuredVersion}"
  testImplementation 'org.assertj:assertj-core'
  testImplementation "com.playtika.testcontainers:embedded-keycloak:${testcontainersVersion}"
  testImplementation "com.playtika.testcontainers:embedded-postgresql:${testcontainersVersion}"
}

compileJava {
  // so that spring-boot config processor picks up meta data
  inputs.files(processResources)
}

bootJar {
  excludeDevtools = true
}

org.hibernate:hibernate-java8 is now empty as the functionality has been merged into hibernate-core. I'd recommend you remove the dependency entirely.

In the future, please use Gradle's built in support for diagnosing this sort of problem. You can use dependencyInsight to see that hibernate-java8 was involved in the dependency being present:

$ gradle dependencyInsight --dependency javax.persistence:javax.persistence-api

> Task :dependencyInsight
javax.persistence:javax.persistence-api:2.2
   variant "compile" [
      org.gradle.status              = release (not requested)
      org.gradle.usage               = java-api
      org.gradle.libraryelements     = jar (compatible with: classes)
      org.gradle.category            = library

      Requested attributes not found in the selected variant:
         org.gradle.dependency.bundling = external
         org.gradle.jvm.version         = 8
   ]
   Selection reasons:
      - Selected by rule
      - By constraint

javax.persistence:javax.persistence-api:2.2
+--- org.hibernate:hibernate-core:5.4.13.Final
|    +--- org.springframework.boot:spring-boot-starter-data-jpa:2.3.0.M4 (requested org.hibernate:hibernate-core)
|    |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-data-jpa)
|    |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4
|    |         +--- org.springframework.boot:spring-boot-starter-web:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-web)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-jetty:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-jetty)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-actuator:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-actuator)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-security:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-security)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-data-jpa:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-oauth2-resource-server:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-oauth2-resource-server)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-oauth2-client:2.3.0.M4
|    |         |    +--- compileClasspath (requested org.springframework.boot:spring-boot-starter-oauth2-client)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-json:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter-web:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-aop:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter-data-jpa:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-jdbc:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter-data-jpa:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter-web:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-actuator:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-security:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-oauth2-resource-server:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-oauth2-client:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-json:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-aop:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-starter-jdbc:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter-actuator:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-autoconfigure:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter:2.3.0.M4 (*)
|    |         |    +--- org.zalando:problem-spring-web-autoconfigure:0.25.2 (requested org.springframework.boot:spring-boot-autoconfigure:2.2.0.RELEASE)
|    |         |    |    \--- org.zalando:problem-spring-web-starter:0.25.2
|    |         |    |         \--- compileClasspath
|    |         |    +--- org.springdoc:springdoc-openapi-common:1.2.33 (requested org.springframework.boot:spring-boot-autoconfigure:2.2.4.RELEASE)
|    |         |    |    \--- org.springdoc:springdoc-openapi-webmvc-core:1.2.33
|    |         |    |         \--- org.springdoc:springdoc-openapi-ui:1.2.33
|    |         |    |              \--- compileClasspath
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter:2.3.0.M4 (*)
|    |         |    +--- org.springframework.boot:spring-boot-autoconfigure:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-starter-logging:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-starter:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         +--- org.springframework.boot:spring-boot-actuator:2.3.0.M4
|    |         |    +--- org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.0.M4 (*)
|    |         |    \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    |         \--- org.springframework.boot:spring-boot-devtools:2.3.0.M4
|    |              +--- compileClasspath (requested org.springframework.boot:spring-boot-devtools)
|    |              \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    +--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
|    \--- org.hibernate:hibernate-java8:5.4.13.Final
|         +--- compileClasspath (requested org.hibernate:hibernate-java8)
|         \--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)
\--- org.springframework.boot:spring-boot-dependencies:2.3.0.M4 (*)

(*) - dependencies omitted (listed previously)
holgerstolzenberg commented 4 years ago

Fix confirmed - removed dependency completely. Thanks for pointing me in the right direction. Didn't know about dependencyInsight which is quite useful.