From ddeeb9f172177d2db4e6825a237aadfd9a6fb728 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 24 Oct 2025 07:31:27 -0600 Subject: [PATCH] HHH-19894 - Use Java 25 for building HHH-19830 - Use of markdown for Javadoc + Switch the Javadoc theme for JDK 25 --- .github/workflows/ci-report.yml | 2 +- .github/workflows/ci.yml | 16 +- .github/workflows/codeql.yml | 2 +- Jenkinsfile | 13 +- ci/build.sh | 9 +- ci/jpa-3.2-tck.Jenkinsfile | 4 +- ci/release/Jenkinsfile | 2 +- ci/snapshot-publish.Jenkinsfile | 2 +- gradle.properties | 4 +- .../org/hibernate/EnabledFetchProfile.java | 107 +++---- .../org/hibernate/IdentifierLoadAccess.java | 127 ++++---- .../java/org/hibernate/SessionBuilder.java | 63 ++-- .../annotations/AnyDiscriminator.java | 52 ++-- .../java/org/hibernate/annotations/Array.java | 25 +- .../java/org/hibernate/annotations/Bag.java | 31 +- .../engine/creation/CommonBuilder.java | 281 ++++++++---------- .../engine/creation/CommonSharedBuilder.java | 47 ++- .../internal/StatelessSessionBuilderImpl.java | 5 + .../src/main/groovy/local.javadoc.gradle | 11 +- migration-guide.adoc | 7 + .../hibernate-gradle-plugin.gradle | 5 + 21 files changed, 383 insertions(+), 432 deletions(-) diff --git a/.github/workflows/ci-report.yml b/.github/workflows/ci-report.yml index e1faf992097f..5a56b1c3d9e7 100644 --- a/.github/workflows/ci-report.yml +++ b/.github/workflows/ci-report.yml @@ -25,7 +25,7 @@ jobs: uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - name: Generate cache key id: cache-key diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06350b10ca61..cfa18d3f560f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: build: permissions: contents: read - name: OpenJDK 21 - ${{matrix.rdbms}} + name: OpenJDK 25 - ${{matrix.rdbms}} runs-on: ubuntu-latest strategy: fail-fast: false @@ -63,11 +63,11 @@ jobs: env: RDBMS: ${{ matrix.rdbms }} run: ci/database-start.sh - - name: Set up Java 21 + - name: Set up Java 25 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - name: Generate cache key id: cache-key @@ -147,7 +147,7 @@ jobs: otp: permissions: contents: read - name: GraalVM 21 - ${{matrix.rdbms}} + name: GraalVM 25 - ${{matrix.rdbms}} runs-on: [ self-hosted, Linux, X64, OracleTestPilot ] strategy: fail-fast: false @@ -161,11 +161,11 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - name: Set up Java 21 + - name: Set up Java 25 uses: graalvm/setup-graalvm@aafbedb8d382ed0ca6167d3a051415f20c859274 # v1.2.8 with: distribution: 'graalvm' - java-version: '21' + java-version: '25' - name: Generate cache key id: cache-key run: | @@ -270,11 +270,11 @@ jobs: persist-credentials: false - name: Reclaim disk space and sanitize user home run: .github/ci-prerequisites-atlas.sh - - name: Set up Java 21 + - name: Set up Java 25 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - name: Generate cache key id: cache-key diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 89ed21898618..ee4e0e81c9f0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,7 +40,7 @@ jobs: uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/Jenkinsfile b/Jenkinsfile index e4d60d35846e..a09447ed07b6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,7 @@ import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper @Library('hibernate-jenkins-pipeline-helpers') _ import org.hibernate.jenkins.pipeline.helpers.job.JobHelper -@Field final String DEFAULT_JDK_VERSION = '21' +@Field final String DEFAULT_JDK_VERSION = '25' @Field final String DEFAULT_JDK_TOOL = "OpenJDK ${DEFAULT_JDK_VERSION} Latest" @Field final String NODE_PATTERN_BASE = 'Worker&&Containers' @Field List environments @@ -40,13 +40,14 @@ stage('Configure') { // Don't build with HANA by default, but only do it nightly until we receive a 3rd instance // new BuildEnvironment( dbName: 'hana_cloud', dbLockableResource: 'hana-cloud', dbLockResourceAsHost: true ), new BuildEnvironment( node: 's390x' ), - // We generally build with JDK 21, but our baseline is Java 17, so we test with JDK 17, to be sure everything works. - // Here we even compile the main code with JDK 17, to be sure no JDK 18+ classes are depended on. - new BuildEnvironment( mainJdkVersion: '17', testJdkVersion: '17' ), + // We generally build with JDK 25, but our baseline is Java 17, so we test with JDK 17, to be sure everything works. + new BuildEnvironment( mainJdkVersion: '25', testJdkVersion: '17' ), + // Additionally, have one job that builds using JDK 17 as well + new BuildEnvironment( mainJdkVersion: '17', additionalOptions: '-Porm.jdk.min=17' ), + new BuildEnvironment( mainJdkVersion: '25', testJdkVersion: '21' ), // We want to enable preview features when testing newer builds of OpenJDK: // even if we don't use these features, just enabling them can cause side effects // and it's useful to test that. - new BuildEnvironment( testJdkVersion: '24', testJdkLauncherArgs: '--enable-preview', additionalOptions: '-PskipJacoco=true' ), new BuildEnvironment( testJdkVersion: '25', testJdkLauncherArgs: '--enable-preview', additionalOptions: '-PskipJacoco=true' ), // The following JDKs aren't supported by Hibernate ORM out-of-the box yet: // they require the use of -Dnet.bytebuddy.experimental=true. @@ -170,7 +171,7 @@ stage('Build') { } stage('Test') { String args = "${buildEnv.additionalOptions ?: ''} ${state[buildEnv.tag]['additionalOptions'] ?: ''}" - withEnv(["RDBMS=${buildEnv.dbName}"]) { + withEnv(["RDBMS=${buildEnv.dbName}", 'CI_SYSTEM=jenkins']) { tryFinally({ if (buildEnv.dbLockableResource == null) { withCredentials([file(credentialsId: 'sybase-jconnect-driver', variable: 'jconnect_driver')]) { diff --git a/ci/build.sh b/ci/build.sh index 914f5e438afa..ef52ed93dd94 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -3,9 +3,12 @@ goal= if [ "$RDBMS" == "h2" ] || [ "$RDBMS" == "" ]; then # This is the default. - goal="preVerifyRelease" - # Settings needed for `preVerifyRelease` execution - for asciidoctor doc rendering - export GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8' + # - special check for Jenkins CI jobs where we don't want to run preVerifyRelease + if [[ -n "$CI_SYSTEM" && "$CI_SYSTEM" != "jenkins" ]]; then + goal="preVerifyRelease" + # Settings needed for `preVerifyRelease` execution - for asciidoctor doc rendering + export GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8' + fi elif [ "$RDBMS" == "hsqldb" ] || [ "$RDBMS" == "hsqldb_2_6" ]; then goal="-Pdb=hsqldb" elif [ "$RDBMS" == "mysql" ] || [ "$RDBMS" == "mysql_8_0" ]; then diff --git a/ci/jpa-3.2-tck.Jenkinsfile b/ci/jpa-3.2-tck.Jenkinsfile index 111f58fb8967..c617e8337618 100644 --- a/ci/jpa-3.2-tck.Jenkinsfile +++ b/ci/jpa-3.2-tck.Jenkinsfile @@ -22,7 +22,7 @@ else { pipeline { agent none tools { - jdk 'OpenJDK 21 Latest' + jdk 'OpenJDK 25 Latest' } options { rateLimitBuilds(throttle: [count: throttleCount, durationName: 'day', userBoost: true]) @@ -30,7 +30,7 @@ pipeline { disableConcurrentBuilds(abortPrevious: true) } parameters { - choice(name: 'IMAGE_JDK', choices: ['jdk17', 'jdk21'], description: 'The JDK base image version to use for the TCK image.') + choice(name: 'IMAGE_JDK', choices: ['jdk17', 'jdk25'], description: 'The JDK base image version to use for the TCK image.') string(name: 'TCK_VERSION', defaultValue: '3.2.0', description: 'The version of the Jakarta JPA TCK i.e. `2.2.0` or `3.0.1`') string(name: 'TCK_SHA', defaultValue: '', description: 'The SHA256 of the Jakarta JPA TCK that is distributed under https://download.eclipse.org/jakartaee/persistence/3.1/jakarta-persistence-tck-${TCK_VERSION}.zip.sha256') string(name: 'TCK_URL', defaultValue: 'https://www.eclipse.org/downloads/download.php?file=/ee4j/jakartaee-tck/jakartaee11/staged/eftl/jakarta-persistence-tck-3.2.0.zip&mirror_id=1', description: 'The URL from which to download the TCK ZIP file. Only needed for testing staged builds. Ensure the TCK_VERSION variable matches the ZIP file name suffix.') diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index cb81ccc7b899..f2baecc4b083 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -70,7 +70,7 @@ pipeline { cron('0 0 * * 0') } tools { - jdk 'OpenJDK 21 Latest' + jdk 'OpenJDK 25 Latest' } options { buildDiscarder logRotator(daysToKeepStr: '30', numToKeepStr: '10') diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index 8b54b31e858c..554f05cab41c 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -23,7 +23,7 @@ pipeline { label 'Release' } tools { - jdk 'OpenJDK 21 Latest' + jdk 'OpenJDK 25 Latest' } options { rateLimitBuilds(throttle: [count: 1, durationName: 'hour', userBoost: true]) diff --git a/gradle.properties b/gradle.properties index 2ff3f882adb8..55e1337400f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -27,9 +27,9 @@ org.gradle.java.installations.auto-download=false # externalized definition of JDK versions so that they are available in both Project (build.gradle) and Settings (settings.gradle) orm.jdk.base=17 -orm.jdk.min=21 +orm.jdk.min=25 # See gradlew/wrapper/gradle-wrapper.properties, https://docs.gradle.org/current/userguide/compatibility.html#java_runtime -orm.jdk.max=22 +orm.jdk.max=25 # The minimum version of Gradle supported for the ORM Gradle plugin. # This is the version used in the plugin tests, used to make sure we do not break compatibility. diff --git a/hibernate-core/src/main/java/org/hibernate/EnabledFetchProfile.java b/hibernate-core/src/main/java/org/hibernate/EnabledFetchProfile.java index b26ed6b4c48f..2ea2858bb5bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/EnabledFetchProfile.java +++ b/hibernate-core/src/main/java/org/hibernate/EnabledFetchProfile.java @@ -7,59 +7,60 @@ import jakarta.persistence.FindOption; import org.hibernate.query.SelectionQuery; -/** - * A {@link jakarta.persistence.FindOption} which requests a named - * {@linkplain org.hibernate.annotations.FetchProfile fetch profile}. - *

- * An instance of this class may be obtained in a type safe way - * from the static metamodel for the class annotated - * {@link org.hibernate.annotations.FetchProfile @FetchProfile}. - *

- * For example, this class defines a fetch profile: - *

- * @Entity
- * @FetchProfile(name = "WithAuthors")
- * class Book {
- *     ...
- *
- *     @ManyToMany
- *     @FetchProfileOverride(profile = Book_.PROFILE_WITH_AUTHORS)
- *     Set<Author> authors;
- * }
- * 
- *

- * An {@code EnabledFetchProfile} may be obtained from the static - * metamodel for the entity {@code Book} and passed as an option to - * {@link Session#find(Class, Object, FindOption...) find()}. - *

- * Book bookWithAuthors =
- *         session.find(Book.class, isbn, Book_._WithAuthors)
- * 
- * Alternatively, it may be {@linkplain #enable(Session) applied} - * to a {@code Session} or {@code Query}. - *
- * Book_._WithAuthors.enable(session);
- * Book bookWithAuthors = session.find(Book.class, isbn);
- * 
- *

- * When the static metamodel is not used, an {@code EnabledFetchProfile} - * may be instantiated directly, passing the name of the fetch profile - * as a string. - *

- * Book bookWithAuthors =
- *         session.find(Book.class, isbn,
- *                      new EnabledFetchProfile("WithAuthors"))
- * 
- * - * @param profileName the {@linkplain org.hibernate.annotations.FetchProfile#name profile name} - * - * @since 7.0 - * - * @see org.hibernate.annotations.FetchProfile - * @see Session#find(Class, Object, FindOption...) - * - * @author Gavin King - */ +/// A [jakarta.persistence.FindOption] which represents a named +/// [fetch profile][org.hibernate.annotations.FetchProfile]. +/// +/// An instance of this class may be obtained in a type safe way +/// from the static metamodel for the class annotated with the +/// [@FetchProfile][org.hibernate.annotations.FetchProfile]. +/// +/// For example, this class defines a fetch profile: +/// ```java +/// @Entity +/// @FetchProfile(name = "WithAuthors") +/// class Book { +/// ... +/// @ManyToMany +/// @FetchProfileOverride(profile = Book_.PROFILE_WITH_AUTHORS) +/// Set authors; +/// } +/// ``` +/// +/// An `EnabledFetchProfile` may be obtained from the static +/// metamodel for the entity {@code Book} and passed as an option to +/// [Session#find(Class, Object, FindOption...)]. +/// +/// ```java +/// Book bookWithAuthors = +/// session.find(Book.class, isbn, Book_._WithAuthors) +/// ``` +/// +/// Alternatively, it may be [applied][#enable(Session)] +/// to a `Session` or `Query`. +/// +/// ```java +/// Book_._WithAuthors.enable(session); +/// Book bookWithAuthors = session.find(Book.class, isbn); +/// ``` +/// +/// When the static metamodel is not used, an `EnabledFetchProfile` +/// may be instantiated directly, passing the name of the fetch profile +/// as a string. +/// +/// ```java +/// Book bookWithAuthors = +/// session.find(Book.class, isbn, +/// new EnabledFetchProfile("WithAuthors")) +/// ``` +/// +/// @param profileName the [profile name][org.hibernate.annotations.FetchProfile#name]. +/// +/// @since 7.0 +/// +/// @see org.hibernate.annotations.FetchProfile +/// @see Session#find(Class, Object, FindOption...) +/// +/// @author Gavin King public record EnabledFetchProfile(String profileName) implements FindOption { diff --git a/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java index 6e544faaad54..a9b8f294371c 100644 --- a/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/IdentifierLoadAccess.java @@ -12,88 +12,73 @@ import jakarta.persistence.Timeout; import org.hibernate.graph.GraphSemantic; -/** - * Loads an entity by its primary identifier. - *

- * The interface is especially useful when customizing association - * fetching using an {@link jakarta.persistence.EntityGraph}. - *

- * var graph = session.createEntityGraph(Book.class);
- * graph.addSubgraph(Book_.publisher);
- * graph.addPluralSubgraph(Book_.authors)
- *     .addSubgraph(Author_.person);
- *
- * Book book =
- *         session.byId(Book.class)
- *             .withFetchGraph(graph)
- *             .load(bookId);
- * 
- *

- * It's also useful for loading entity instances with a specific - * {@linkplain CacheMode cache interaction mode} in effect, or in - * {@linkplain Session#setReadOnly(Object, boolean) read-only mode}. - *

- * Book book =
- *         session.byId(Book.class)
- *             .with(CacheMode.GET)
- *             .withReadOnly(true)
- *             .load(bookId);
- * 
- * - * @author Eric Dalquist - * @author Steve Ebersole - * - * @see Session#byId - * - * @deprecated Use forms of {@linkplain Session#find} accepting - * {@linkplain jakarta.persistence.FindOption} instead of {@linkplain Session#byId}. - */ +/// Loads an entity by its primary identifier. +/// +/// The interface is especially useful when customizing association +/// fetching using an [jakarta.persistence.EntityGraph]. +/// ```java +/// var graph = session.createEntityGraph(Book.class); +/// graph.addSubgraph(Book_.publisher); +/// graph.addPluralSubgraph(Book_.authors) +/// .addSubgraph(Author_.person); +/// Book book = session.byId(Book.class) +/// .withFetchGraph(graph) +/// .load(bookId); +/// ``` +/// +/// It's also useful for loading entity instances with a specific +/// [cache interaction mode][CacheMode] in effect, or in +/// [read-only mode][Session#setReadOnly(Object, boolean)]. +/// +/// ```java +/// Book book = session.byId(Book.class) +/// .with(CacheMode.GET) +/// .withReadOnly(true) +/// .load(bookId); +/// ``` +/// +/// @author Eric Dalquist +/// @author Steve Ebersole +/// +/// @see Session#byId +/// +/// @deprecated Use forms of [Session#find] accepting [jakarta.persistence.FindOption]} instead. @Deprecated(since = "7.1", forRemoval = true) public interface IdentifierLoadAccess { - /** - * Specify the {@linkplain LockMode lock mode} to use when - * querying the database. - * - * @param lockMode The lock mode to apply - * @return {@code this}, for method chaining - */ + /// Specify the [lock mode][LockMode] to use when querying the database. + /// + /// @param lockMode The lock mode to apply + /// + /// @return `this`, for method chaining default IdentifierLoadAccess with(LockMode lockMode) { return with( lockMode, PessimisticLockScope.NORMAL ); } - /** - * Specify the {@linkplain LockMode lock mode} to use when - * querying the database. - * - * @param lockMode The lock mode to apply - * - * @return {@code this}, for method chaining - */ + /// Specify the [lock mode][LockMode] and [scope][PessimisticLockScope] to use when querying the database. + /// + /// @param lockMode The lock mode to apply + /// @param lockScope The locking scope (how much to lock). + /// + /// @return `this`, for method chaining IdentifierLoadAccess with(LockMode lockMode, PessimisticLockScope lockScope); - /** - * Specify the {@linkplain Timeout timeout} to use when - * querying the database. - * - * @param timeout The timeout to apply to the database operation - * - * @return {@code this}, for method chaining - */ + /// Specify the [timeout][Timeout] to use when querying the database. + /// + /// @param timeout The timeout to apply to the database operation + /// + /// @return `this`, for method chaining IdentifierLoadAccess with(Timeout timeout); - /** - * Specify the {@linkplain LockOptions lock options} to use when - * querying the database. - * - * @param lockOptions The lock options to use - * - * @return {@code this}, for method chaining - * - * @deprecated Use one of {@linkplain #with(LockMode)}, - * {@linkplain #with(LockMode, PessimisticLockScope)} - * and/or {@linkplain #with(Timeout)} instead. - */ + /// Specify the [lock options][LockOptions] to use when querying the database. + /// + /// @param lockOptions The lock options to use + /// + /// @return `this`, for method chaining + /// + /// @deprecated Use one of [#with(LockMode)], + /// [#with(LockMode, PessimisticLockScope)] + /// and/or [#with(Timeout)] instead. @Deprecated(since = "7.0", forRemoval = true) IdentifierLoadAccess with(LockOptions lockOptions); diff --git a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java index d6892e78b0bc..87037bb8b111 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionBuilder.java @@ -12,39 +12,40 @@ import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; -/** - * Allows creation of a new {@link Session} with specific options - * overriding the defaults from the {@link SessionFactory}. - *
- * try (var session =
- *         sessionFactory.withOptions()
- *             .tenantIdentifier(tenantId)
- *             .initialCacheMode(CacheMode.PUT)
- *             .flushMode(FlushMode.COMMIT)
- *             .interceptor(new Interceptor() {
- *                 @Override
- *                 public void preFlush(Iterator<Object> entities) {
- *                     ...
- *                 }
- *             })
- *             .openSession()) {
- *     ...
- * }
- * 
- * - * @author Steve Ebersole - * - * @see SessionFactory#withOptions() - * @see SharedSessionBuilder - */ +/// Allows creation of a new [Session] with specific options +/// overriding the defaults from the [SessionFactory]. +/// +/// ```java +/// try (var session = sessionFactory.withOptions() +/// .tenantIdentifier(tenantId) +/// .initialCacheMode(CacheMode.PUT) +/// .flushMode(FlushMode.COMMIT) +/// .interceptor(new Interceptor() { +/// @Override +/// public void preFlush(Iterator entities) { +/// ... +/// } +/// }) +/// .openSession()) { +/// ... +/// } +/// } +/// ``` +/// +/// @see SessionFactory#withOptions() +/// @see SharedSessionBuilder +/// +/// @author Steve Ebersole public interface SessionBuilder extends CommonBuilder { - /** - * Opens a session with the specified options. - * - * @return The session - */ + /// Open the session using the specified options. + /// @see #open Session openSession(); + @Override + default Session open() { + return openSession(); + } + @Override SessionBuilder interceptor(Interceptor interceptor); @@ -81,6 +82,7 @@ public interface SessionBuilder extends CommonBuilder { * * @return {@code this}, for method chaining */ + @Override SessionBuilder connection(Connection connection); /** @@ -93,6 +95,7 @@ public interface SessionBuilder extends CommonBuilder { * * @since 7.0 */ + @Override SessionBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); /** diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/AnyDiscriminator.java b/hibernate-core/src/main/java/org/hibernate/annotations/AnyDiscriminator.java index 9952cd3fffb7..8e7349264ace 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/AnyDiscriminator.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/AnyDiscriminator.java @@ -14,36 +14,32 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * A simplified way to specify the type of the discriminator in an {@link Any} - * mapping, using the JPA-defined {@link DiscriminatorType}. This annotation - * must be used in combination with {@link jakarta.persistence.Column} to fully - * describe the discriminator column for an {@code @Any} relationship. - *

- * {@code @AnyDiscriminator} is quite similar to - * {@link jakarta.persistence.DiscriminatorColumn#discriminatorType()} in - * single-table inheritance mappings, but it describes a discriminator held - * along with the foreign key in the referring side of a discriminated - * relationship. - *

- * This annotation may be used in conjunction with {@link JdbcType} or - * {@link JdbcTypeCode} to more precisely specify the type mapping. On the - * other hand, {@link JdbcType} or {@link JdbcTypeCode} may be used without - * {@code @AnyDiscriminator}. - * - * @see Any - * @see AnyDiscriminatorValue - * @see AnyDiscriminatorImplicitValues - * - * @since 6.0 - */ +/// A simplified way to specify the type of the discriminator in an [Any] +/// mapping, using the JPA-defined [DiscriminatorType]. This annotation +/// must be used in combination with [jakarta.persistence.Column] to fully +/// describe the discriminator column for an `@Any` relationship. +/// +/// `@AnyDiscriminator` is quite similar to +/// [jakarta.persistence.DiscriminatorColumn#discriminatorType()] in +/// single-table inheritance mappings, but it describes a discriminator held +/// along with the foreign key in the referring side of a discriminated +/// relationship. +/// +/// This annotation may be used in conjunction with [JdbcType] or +/// [JdbcTypeCode] to more precisely specify the type mapping. On the +/// other hand, [JdbcType] or [JdbcTypeCode] may be used without +/// `@AnyDiscriminator`. +/// +/// @see Any +/// @see AnyDiscriminatorValue +/// @see AnyDiscriminatorImplicitValues +/// +/// @since 6.0 @Target({METHOD, FIELD, ANNOTATION_TYPE}) @Retention( RUNTIME ) public @interface AnyDiscriminator { - /** - * The type of the discriminator, as a JPA {@link DiscriminatorType}. - * For more precise specification of the type, use {@link JdbcType} - * or {@link JdbcTypeCode}. - */ + /// The type of the discriminator, as a JPA [DiscriminatorType]. + /// For more precise specification of the type, use [JdbcType] + /// or [JdbcTypeCode]. DiscriminatorType value() default DiscriminatorType.STRING; } diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/Array.java b/hibernate-core/src/main/java/org/hibernate/annotations/Array.java index cf8ef8a14d18..797af71b05b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/Array.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/Array.java @@ -13,23 +13,20 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies the maximum length of a SQL array type mapped by - * the annotated attribute. - *

- * For example: - *

- * {@code @Array(length=100)} // the maximum length of the SQL array
- * {@code @Column(length=64)} // the maximum length of the strings in the array
- * String[] strings;
- * 
- */ +/// Specifies the maximum length of a SQL array type mapped by +/// the annotated attribute. +/// +/// For example: +/// +/// ```java +/// @Array(length=100) // the maximum length of the SQL array +/// @Column(length=64) // the maximum length of the strings in the array +/// String[] strings; +/// ``` @Incubating @Target({FIELD, METHOD}) @Retention( RUNTIME ) public @interface Array { - /** - * The maximum length of the array. - */ + /// The maximum length of the array. int length(); } diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/Bag.java b/hibernate-core/src/main/java/org/hibernate/annotations/Bag.java index ce839acc5660..df5322c85c9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/Bag.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/Bag.java @@ -12,23 +12,20 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies that an attribute of type {@link java.util.List} is semantically - * a {@linkplain org.hibernate.metamodel.CollectionClassification#BAG bag}, - * that is, that the order of the list elements is not significant, and should - * not be persistent. - *

- * This annotation is not necessary, and has no effect, unless the configuration - * property {@value org.hibernate.cfg.AvailableSettings#DEFAULT_LIST_SEMANTICS} - * is set to {@link org.hibernate.metamodel.CollectionClassification#LIST}. - * However, its use is still encouraged, since the explicit annotation serves - * as useful documentation. - * - * @apiNote This annotation causes an exception if the attribute is also annotated - * {@link jakarta.persistence.OrderColumn} or {@link ListIndexBase}. - * - * @author Steve Ebersole - */ +/// Specifies that an attribute of type [java.util.List] is semantically a +/// [bag][org.hibernate.metamodel.CollectionClassification#BAG] - that the order of +/// the list elements is not significant, and should not be persistent. +/// +/// This annotation is not necessary, and has no effect, unless the configuration +/// property {@value org.hibernate.cfg.AvailableSettings#DEFAULT_LIST_SEMANTICS} +/// is set to {@link org.hibernate.metamodel.CollectionClassification#LIST}. +/// However, its use is still encouraged, since the explicit annotation serves +/// as useful documentation. +/// +/// @apiNote This annotation causes an exception if the attribute is also annotated +/// [jakarta.persistence.OrderColumn] or [ListIndexBase]. +/// +/// @author Steve Ebersole @Target({METHOD, FIELD, ANNOTATION_TYPE}) @Retention(RUNTIME) public @interface Bag { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java index 44c2bc631295..dc9aedb051ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonBuilder.java @@ -10,7 +10,6 @@ import org.hibernate.Incubating; import org.hibernate.Interceptor; import org.hibernate.Session; -import org.hibernate.SessionFactory; import org.hibernate.SharedSessionContract; import org.hibernate.StatelessSession; @@ -28,187 +27,147 @@ */ @Incubating public interface CommonBuilder { + /// Open the session using the specified options. + SharedSessionContract open(); - /** - * Adds a specific connection to the session options. - * - * @param connection The connection to use. - * - * @return {@code this}, for method chaining - */ + /// Adds a specific connection to be used to the session options. + /// + /// @param connection The connection to use. + /// @return {@code this}, for method chaining CommonBuilder connection(Connection connection); - /** - * Specifies the connection handling modes for the session. - *

- * Note that if {@link ConnectionAcquisitionMode#IMMEDIATELY} is specified, - * then the release mode must be {@link ConnectionReleaseMode#ON_CLOSE}. - * - * @return {@code this}, for method chaining - * - * @since 7.0 - */ + /// Specifies the connection handling modes for the session. + /// + /// @apiNote If [ConnectionAcquisitionMode#IMMEDIATELY] is specified, + /// then the release mode must be [ConnectionReleaseMode#ON_CLOSE]. + /// + /// @return `this`, for method chaining + /// + /// @since 7.0 CommonBuilder connectionHandling(ConnectionAcquisitionMode acquisitionMode, ConnectionReleaseMode releaseMode); - /** - * Adds a specific interceptor to the session options. - * - * @param interceptor The interceptor to use. - * - * @return {@code this}, for method chaining - */ + /// Adds a specific interceptor to the session options. + /// + /// @param interceptor The interceptor to use. + /// @return `this`, for method chaining CommonBuilder interceptor(Interceptor interceptor); - /** - * Specifies that no {@link Interceptor} should be used. - *

- * By default, if no {@code Interceptor} is explicitly - * {@linkplain #interceptor(Interceptor) specified}, the - * {@code Interceptor} associated with the {@link SessionFactory} is - * inherited by the new session. Or, if there is no interceptor - * associated with the {@link SessionFactory}, but a session-scoped - * interceptor has been configured, a new session-scoped - * {@code Interceptor} will be created for the new session. - *

- * Calling {@link #interceptor(Interceptor) interceptor(null)} has the - * same effect. - * - * @return {@code this}, for method chaining - */ + /// Specifies that no {@link Interceptor} should be used. This indicates to + /// ignore both (if either) the [interceptor][org.hibernate.cfg.SessionEventSettings#INTERCEPTOR] + /// and [session-scoped interceptor][org.hibernate.cfg.SessionEventSettings#SESSION_SCOPED_INTERCEPTOR] + /// associated with the `SessionFactory` + /// + /// @return `this`, for method chaining + /// + /// @apiNote Calling [#interceptor(Interceptor)] with `null` has the same effect CommonBuilder noInterceptor(); - /** - * Specifies that no - * {@linkplain org.hibernate.cfg.SessionEventSettings#SESSION_SCOPED_INTERCEPTOR - * session-scoped interceptor} should be instantiated for the new session. - *

- * By default, if no {@link Interceptor} is explicitly - * {@linkplain #interceptor(Interceptor) specified}, and if there - * is no interceptor associated with the {@link SessionFactory}, but - * a session-scoped interceptor has been configured, a new session-scoped - * {@code Interceptor} will be created for the new session. - *

- * Note that this operation does not disable use of an interceptor - * associated with the {@link SessionFactory}. - * - * @return {@code this}, for method chaining - * - * @see org.hibernate.cfg.SessionEventSettings#SESSION_SCOPED_INTERCEPTOR - * - * @since 7.2 - */ + /// Specifies that no [session-scoped interceptor][org.hibernate.cfg.SessionEventSettings#SESSION_SCOPED_INTERCEPTOR] + /// should be used for the session. If the `SessionFactory` has a configured + /// [interceptor][org.hibernate.cfg.SessionEventSettings#INTERCEPTOR], it will still be used. + /// + /// @return `this`, for method chaining + /// + /// @see #noInterceptor + /// + /// @apiNote Unlike [#noInterceptor], this operation does not disable use of an + /// [interceptor][org.hibernate.cfg.SessionEventSettings#INTERCEPTOR] associated with the `SessionFactory`. + /// + /// @since 7.2 CommonBuilder noSessionInterceptorCreation(); - /** - * Applies the given statement inspection function to the session. - * - * @param operator An operator which accepts a SQL string, returning - * a processed SQL string to be used by Hibernate instead of the given - * original SQL. The operator may simply return the original SQL. - * - * @return {@code this}, for method chaining - */ + /// Applies the given statement inspection function to the session. + /// + /// @param operator An operator which accepts a SQL string, returning + /// a processed SQL string to be used by Hibernate instead. The + /// operator may simply (and usually) return the original SQL. + /// + /// @return `this`, for method chaining CommonBuilder statementInspector(UnaryOperator operator); - /** - * Signifies that no SQL statement inspector should be used. - *

- * By default, if no inspector is explicitly specified, the - * inspector associated with the {@link SessionFactory} is - * inherited by the new session. - *

- * Calling {@link #interceptor(Interceptor)} with null has the same effect. - * - * @return {@code this}, for method chaining - */ + /// Signifies that no SQL statement inspector should be used. + /// + /// By default, if no inspector is explicitly specified, the + /// inspector associated with the {@link org.hibernate.SessionFactory}, if one, is + /// inherited by the new session. + /// + /// @return `this`, for method chaining + /// + /// @apiNote Calling [#statementInspector] with `null` has the same effect. CommonBuilder noStatementInspector(); - /** - * Specify the tenant identifier to be associated with the opened session. - *

-	 * try (var session =
-	 *         sessionFactory.withOptions()
-	 *             .tenantIdentifier(tenantId)
-	 *             .openSession()) {
-	 *     ...
-	 * }
-	 * 
- * - * @param tenantIdentifier The tenant identifier. - * - * @return {@code this}, for method chaining - */ + /// Specify the tenant identifier to be associated with the opened session. + /// + /// ```java + /// try (var session = sessionFactory.withOptions() + /// .tenantIdentifier(tenantId) + /// .openSession()) { + /// ... + /// } + /// ``` + /// @param tenantIdentifier The tenant identifier. + /// + /// @return `this`, for method chaining CommonBuilder tenantIdentifier(Object tenantIdentifier); - /** - * Specify a {@linkplain Session#isDefaultReadOnly read-only mode} - * for the session. If a session is created in read-only mode, then - * {@link Connection#setReadOnly} is called when a JDBC connection - * is obtained. - *

- * Furthermore, if read/write replication is in use, then: - *

    - *
  • a read-only session will connect to a read-only replica, but - *
  • a non-read-only session will connect to a writable replica. - *
- *

- * When read/write replication is in use, it's strongly recommended - * that the session be created with the {@linkplain #initialCacheMode - * initial cache mode} set to {@link CacheMode#GET}, to avoid writing - * stale data read from a read-only replica to the second-level cache. - * Hibernate cannot possibly guarantee that data read from a read-only - * replica is up to date. - *

- * When read/write replication is in use, it's possible that an item - * read from the second-level cache might refer to data which does not - * yet exist in the read-only replica. In this situation, an exception - * occurs when the association is fetched. To completely avoid this - * possibility, the {@linkplain #initialCacheMode initial cache mode} - * must be set to {@link CacheMode#IGNORE}. However, it's also usually - * possible to structure data access code in a way which eliminates - * this possibility. - *

- *

-	 * try (var readOnlySession =
-	 *         sessionFactory.withOptions()
-	 *                 .readOnly(true)
-	 *                 .initialCacheMode(CacheMode.IGNORE)
-	 *                 .openSession()) {
-	 *     ...
-	 * }
-	 * 
- *

- * If a session is created in read-only mode, then it cannot be - * changed to read-write mode, and any call to - * {@link Session#setDefaultReadOnly(boolean)} with fail. On the - * other hand, if a session is created in read-write mode, then it - * may later be switched to read-only mode, but all database access - * is directed to the writable replica. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#getReadOnlyConnection(Object) - * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#releaseReadOnlyConnection(Object, Connection) - */ + /// Specify a [read-only mode][Session#isDefaultReadOnly] + /// for the session. If a session is created in read-only mode, then + /// [Connection#setReadOnly] is called when a JDBC connection is obtained. + /// + /// Furthermore, if read/write replication is in use, then: + /// * a read-only session will connect to a read-only replica, but + /// * a non-read-only session will connect to a writable replica. + /// + /// When read/write replication is in use, it's strongly recommended + /// that the session be created with the [initial cache-mode][#initialCacheMode] + /// set to [CacheMode#GET], to avoid writing stale data read from a read-only + /// replica to the second-level cache. Hibernate cannot possibly guarantee that + /// data read from a read-only replica is up to date. + /// + /// When read/write replication is in use, it's possible that an item + /// read from the second-level cache might refer to data which does not + /// yet exist in the read-only replica. In this situation, an exception + /// occurs when the association is fetched. To completely avoid this + /// possibility, the [initial cache-mode][#initialCacheMode] must be + /// set to [CacheMode#IGNORE]. However, it's also usually possible to + /// structure data access code in a way which eliminates this possibility. + /// ```java + /// try (var readOnlySession = + /// sessionFactory.withOptions() + /// .readOnly(true) + /// .initialCacheMode(CacheMode.IGNORE) + /// .openSession()) { + /// ... + /// } + /// ``` + /// + /// If a session is created in read-only mode, then it cannot be + /// changed to read-write mode, and any call to [Session#setDefaultReadOnly(boolean)] + /// with fail. On the other hand, if a session is created in read-write mode, then it + /// may later be switched to read-only mode, but all database access is directed to + /// the writable replica. + /// + /// @return `this`, for method chaining + /// + /// @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#getReadOnlyConnection(Object) + /// @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#releaseReadOnlyConnection(Object, Connection) + /// + /// @since 7.2 @Incubating CommonBuilder readOnly(boolean readOnly); - /** - * Specify the initial {@link CacheMode} for the session. - * - * @return {@code this}, for method chaining - * @since 7.2 - * - * @see SharedSessionContract#getCacheMode() - */ + /// Specify the initial [CacheMode] for the session. + /// + /// @return `this`, for method chaining + /// + /// @see SharedSessionContract#getCacheMode() + /// + /// @since 7.2 CommonBuilder initialCacheMode(CacheMode cacheMode); - /** - * Specify the {@linkplain org.hibernate.cfg.JdbcSettings#JDBC_TIME_ZONE - * JDBC time zone} for the session. - * - * @return {@code this}, for method chaining - */ + /// Specify the [JDBC time zone][org.hibernate.cfg.JdbcSettings#JDBC_TIME_ZONE] + /// to use for the session. + /// + /// @return `this`, for method chaining CommonBuilder jdbcTimeZone(TimeZone timeZone); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java index aff7b5b9cc13..95ebd2e24107 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/CommonSharedBuilder.java @@ -9,50 +9,37 @@ import org.hibernate.ConnectionReleaseMode; import org.hibernate.Incubating; import org.hibernate.Interceptor; -import org.hibernate.Session; -import org.hibernate.StatelessSession; import java.sql.Connection; import java.util.TimeZone; import java.util.function.UnaryOperator; -/** - * Common options for builders of {@linkplain Session stateful} - * and {@linkplain StatelessSession stateless} sessions - * which share state from an underlying session. - * - * @since 7.2 - * - * @author Steve Ebersole - */ +/// Common options for builders of [stateful][org.hibernate.Session] and +/// [stateless][org.hibernate.StatelessSession] sessions which share state +/// from an underlying stateful/stateless session. +/// +/// @since 7.2 +/// +/// @author Steve Ebersole @Incubating public interface CommonSharedBuilder extends CommonBuilder { - /** - * Signifies that the connection from the original session should be used to create the new session. - * Implies that the overall "transaction context" should be shared as well. - * - * @return {@code this}, for method chaining - */ + /// Signifies that the connection from the original session should be used to create the new session. + /// Implies that the overall "transaction context" should be shared as well. + /// + /// @return `this`, for method chaining CommonSharedBuilder connection(); - /** - * Signifies the interceptor from the original session should be used to create the new session. - * - * @return {@code this}, for method chaining - */ + /// Signifies the interceptor from the original session should be used to create the new session. + /// + /// @return `this`, for method chaining CommonSharedBuilder interceptor(); - /** - * Signifies that the SQL {@linkplain org.hibernate.resource.jdbc.spi.StatementInspector statement inspector} - * from the original session should be used. - */ + /// Signifies that the SQL statement inspector from the original session should be used to create the new session. + /// + /// @return `this`, for method chaining CommonSharedBuilder statementInspector(); - /** - * Signifies that no SQL {@linkplain org.hibernate.resource.jdbc.spi.StatementInspector statement inspector} - * should be used. - */ @Override CommonSharedBuilder noStatementInspector(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java index 38ac90bcf9ed..40fc388ebcfa 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/StatelessSessionBuilderImpl.java @@ -40,6 +40,11 @@ protected StatelessSessionBuilder getThis() { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // StatelessSessionBuilder + @Override + public StatelessSession open() { + return openStatelessSession(); + } + @Override public StatelessSession openStatelessSession() { CORE_LOGGER.openingStatelessSession( tenantIdentifier ); diff --git a/local-build-plugins/src/main/groovy/local.javadoc.gradle b/local-build-plugins/src/main/groovy/local.javadoc.gradle index f732ede290f5..cba634da030a 100644 --- a/local-build-plugins/src/main/groovy/local.javadoc.gradle +++ b/local-build-plugins/src/main/groovy/local.javadoc.gradle @@ -11,7 +11,7 @@ configurations { } dependencies { - themezip 'org.hibernate.infra:hibernate-asciidoctor-theme:6.0.2.Final@zip' + themezip 'org.hibernate.infra:hibernate-asciidoctor-theme:6.1.1.Final@zip' } tasks.register('unpackTheme', Copy) { @@ -44,8 +44,9 @@ tasks.withType(Javadoc).configureEach { windowTitle = "Hibernate Javadocs ($project.name)" docTitle = "Hibernate Javadocs ($project.name : $project.version)" // Pick the styles for the JDK that is used to "build" the Javadocs: - stylesheetFile = rootProject.layout.buildDirectory.dir("unpacked-theme").get() - .dir("hibernate-asciidoctor-theme").dir("javadoc").dir("jdk21").file("stylesheet.css").asFile + + def rootThemeDir = rootProject.layout.buildDirectory.dir("unpacked-theme").get().dir("hibernate-asciidoctor-theme").dir("javadoc").dir("jdk25") + stylesheetFile = rootThemeDir.file("stylesheet.css").asFile bottom = "Copyright © 2001-$currentYear Red Hat, Inc. All Rights Reserved." // The javadoc folder contains cached versions of the respective element-list files to avoid release issues when servers are down @@ -70,6 +71,10 @@ tasks.withType(Javadoc).configureEach { quiet() addBooleanOption('Xdoclint:none', true) + + header = "

" + addStringOption '-add-script', rootThemeDir.dir('resources').file('theme-switch.js').asFile.absolutePath + docFilesSubDirs = true } dependsOn tasks.unpackTheme } diff --git a/migration-guide.adoc b/migration-guide.adoc index 4004f5200341..d65de8bf5fc2 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -22,6 +22,13 @@ earlier versions, see any other pertinent migration guides as well. See the link:{releaseSeriesBase}[website] for the list of requirements for the {version} series. +[[requirements-building]] +=== Building + +Starting with version 7.2, Gradle 9 and Java 25 are required to build the project. +However, the produced artifacts are still compatible with Java 17 and the produced Gradle plugin is still compatible with Gradle 8. + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // New Features // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle index 695ec82f7440..b75be0dfdb29 100644 --- a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle +++ b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle @@ -69,6 +69,11 @@ test { "--add-opens", "java.base/java.util.concurrent.atomic=ALL-UNNAMED") } +if ( jdkVersions.test.launcher.asInt() > 21 ) { + project.logger.lifecycle( "Skipping {} tests for JDK version {}", project.name, jdkVersions.test.launcher.asInt() ) + tasks.getByName( "test" ).enabled = false +} + def releasePrepareTask = tasks.register("releasePrepare") { group "release-prepare" description "See :release:releasePrepare for details. Here we hook in the `check` task."