From 4f9035e9f822474efe050b7a57737adecf3bd00a Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 26 Sep 2024 11:47:37 -0500 Subject: [PATCH] HHH-18545 - Document "characteristics" of settings --- documentation/documentation.gradle | 2 +- .../java/org/hibernate/cfg/Compatibility.java | 23 ++++++ .../java/org/hibernate/cfg/QuerySettings.java | 42 ++++++----- .../hibernate/cfg/TransactionSettings.java | 70 +++++++++++-------- .../main/java/org/hibernate/cfg/Unsafe.java | 22 ++++++ .../orm/properties/AsciiDocWriter.java | 6 ++ .../orm/properties/SettingDescriptor.java | 20 +++++- .../orm/properties/SettingWorkingDetails.java | 22 +++++- .../org/hibernate/orm/properties/Utils.java | 37 ++++++++++ .../properties/jdk11/SettingsCollector.java | 28 ++------ .../properties/jdk17/SettingsCollector.java | 29 ++------ 11 files changed, 205 insertions(+), 96 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/Compatibility.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/Unsafe.java diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index 9af4fee5b5..ab3342725d 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -573,7 +573,7 @@ settingsDocumentation { } transaction { explicitPosition = 6 - summary = "Proxool Connection Pool Settings" + summary = "Transaction Environment Settings" description = "Settings which control how Hibernate interacts with and manages transactions" settingsClassName "org.hibernate.cfg.TransactionSettings" } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Compatibility.java b/hibernate-core/src/main/java/org/hibernate/cfg/Compatibility.java new file mode 100644 index 0000000000..efddfc6293 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Compatibility.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.cfg; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + * Denotes that a setting is intended to allow applications to upgrade + * versions of Hibernate and maintain backwards compatibility with the + * older version in some specific behavior. Such settings are almost always + * considered temporary and are usually also {@linkplain Deprecated deprecated}. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Compatibility { +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java index 71e529c80e..adb2976581 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java @@ -18,7 +18,8 @@ import jakarta.persistence.criteria.CriteriaUpdate; public interface QuerySettings { /** * Boolean setting to control if the use of tech preview JSON functions in HQL is enabled. - * By default, this is {@code false} i.e. disabled since the functions are still incubating. + * + * @settingDefault {@code false} (disabled) since the functions are still incubating. * * @since 7.0 */ @@ -27,7 +28,8 @@ public interface QuerySettings { /** * Boolean setting to control if the use of tech preview XML functions in HQL is enabled. - * By default, this is {@code false} i.e. disabled since the functions are still incubating. + * + * @settingDefault {@code false} (disabled) since the functions are still incubating. * * @since 7.0 */ @@ -39,6 +41,8 @@ public interface QuerySettings { * databases. By default, integer division in HQL can produce a non-integer * on Oracle, MySQL, or MariaDB. * + * @settingDefault {@code false} + * * @since 6.5 */ String PORTABLE_INTEGER_DIVISION = "hibernate.query.hql.portable_integer_division"; @@ -70,10 +74,10 @@ public interface QuerySettings { /** * When enabled, specifies that named queries be checked during startup. *

- * By default, named queries are checked at startup. - *

* Mainly intended for use in test environments. * + * @settingDefault {@code true} (enabled) - named queries are checked at startup. + * * @see org.hibernate.boot.SessionFactoryBuilder#applyNamedQueryCheckingOnStartup(boolean) */ String QUERY_STARTUP_CHECKING = "hibernate.query.startup_check"; @@ -102,8 +106,8 @@ public interface QuerySettings { * {@link jakarta.persistence.Query#setParameter(jakarta.persistence.Parameter,Object)} * to specify its argument are passed to JDBC using a bind parameter. * - *

- * The default mode is {@link org.hibernate.query.criteria.ValueHandlingMode#BIND}. + * + * @settingDefault {@link org.hibernate.query.criteria.ValueHandlingMode#BIND}. * * @since 6.0.0 * @@ -119,8 +123,8 @@ public interface QuerySettings { * of null values} sorted via the HQL {@code ORDER BY} clause, either {@code none}, * {@code first}, or {@code last}, or an instance of the enumeration * {@link jakarta.persistence.criteria.Nulls}. - *

- * The default is {@code none}. + * + * @settingDefault {@code none}. * * @see jakarta.persistence.criteria.Nulls * @see org.hibernate.boot.SessionFactoryBuilder#applyDefaultNullPrecedence(jakarta.persistence.criteria.Nulls) @@ -155,8 +159,8 @@ public interface QuerySettings { /** * When enabled, ordinal parameters (represented by the {@code ?} placeholder) in * native queries will be ignored. - *

- * By default, native queries are checked for ordinal placeholders. + * + * @settingDefault {@code false} (disabled) - native queries are checked for ordinal placeholders. * * @see SessionFactoryOptions#getNativeJdbcParametersIgnored() */ @@ -167,12 +171,14 @@ public interface QuerySettings { * {@link java.sql.Time}, and {@link java.sql.Timestamp} instead of the * datetime types from {@link java.time}, recovering the behavior of * native queries in Hibernate 6 and earlier. - *

- * By default, native queries return {@link java.time.LocalDate}, - * {@link java.time.LocalTime}, and {@link java.time.LocalDateTime}. + * + * @settingDefault {@code false} (disabled) - native queries return + * {@link java.time.LocalDate}, {@link java.time.LocalTime}, and + * {@link java.time.LocalDateTime}. * * @since 7.0 */ + @Compatibility String NATIVE_PREFER_JDBC_DATETIME_TYPES = "hibernate.query.native.prefer_jdbc_datetime_types"; /** @@ -183,9 +189,9 @@ public interface QuerySettings { *

* When enabled, this setting specifies that an exception should be thrown for any * query which would result in the limit being applied in-memory. - *

- * By default, the exception is disabled, and the possibility of terrible - * performance is left as a problem for the client to avoid. + * + * @settingDefault {@code false} (disabled) - no exception is thrown and the + * possibility of terrible performance is left as a problem for the client to avoid. * * @since 5.2.13 */ @@ -203,8 +209,8 @@ public interface QuerySettings { *

  • {@link org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode#EXCEPTION "exception"} * specifies that a {@link org.hibernate.HibernateException} should be thrown. * - *

    - * By default, a warning is logged. + * + * @settingDefault {@link org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode#WARNING "warning"} * * @since 5.2.17 * diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java index 4f928ba16f..2a11ddca74 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java @@ -4,6 +4,7 @@ */ package org.hibernate.cfg; + import jakarta.persistence.spi.PersistenceUnitInfo; /** @@ -127,36 +128,6 @@ public interface TransactionSettings { */ String ALLOW_JTA_TRANSACTION_ACCESS = "hibernate.jta.allowTransactionAccess"; - /** - * Allows a detached proxy or lazy collection to be fetched even when not - * associated with an open persistence context, by creating a temporary - * persistence context when the proxy or collection is accessed. This - * behavior is not recommended, since it can easily break transaction - * isolation or lead to data aliasing; it is therefore disabled by default. - * - * @settingDefault {@code false} (disabled) - * - * @see org.hibernate.boot.SessionFactoryBuilder#applyLazyInitializationOutsideTransaction(boolean) - */ - String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans"; - - /** - * When enabled, allows update operations outside a transaction. - *

    - * Since version 5.2 Hibernate conforms with the JPA specification and disallows - * flushing any update outside a transaction. - *

    - * Values are {@code true}, which allows flushing outside a transaction, and - * {@code false}, which does not. - *

    - * The default behavior is to disallow update operations outside a transaction. - * - * @see org.hibernate.boot.SessionFactoryBuilder#allowOutOfTransactionUpdateOperations(boolean) - * - * @since 5.2 - */ - String ALLOW_UPDATE_OUTSIDE_TRANSACTION = "hibernate.allow_update_outside_transaction"; - /** * When enabled, specifies that the {@link org.hibernate.Session} should be * closed automatically at the end of each transaction. @@ -176,4 +147,43 @@ public interface TransactionSettings { * @see org.hibernate.boot.SessionFactoryBuilder#applyAutoFlushing(boolean) */ String FLUSH_BEFORE_COMPLETION = "hibernate.transaction.flush_before_completion"; + + /** + * Allows a detached proxy or lazy collection to be fetched even when not + * associated with an open persistence context, by creating a temporary + * persistence context when the proxy or collection is accessed. This + * behavior is not recommended, since it can easily break transaction + * isolation or lead to data aliasing; it is therefore disabled by default. + * + * @settingDefault {@code false} (disabled) + * + * @apiNote Generally speaking, all access to transactional data should be done in a transaction. + * + * @see org.hibernate.boot.SessionFactoryBuilder#applyLazyInitializationOutsideTransaction(boolean) + */ + @Unsafe + String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans"; + + /** + * When enabled, allows update operations outside a transaction. + *

    + * Since version 5.2 Hibernate conforms with the JPA specification and disallows + * flushing any update outside a transaction. + *

    + * Values are {@code true}, which allows flushing outside a transaction, and + * {@code false}, which does not. + *

    + * The default behavior is to disallow update operations outside a transaction. + * + * @settingDefault {@code false} (disabled) + * + * @apiNote Generally speaking, all access to transactional data should be done in a transaction. + * Combining this with second-level caching, e.g., will cause problems. + * + * @see org.hibernate.boot.SessionFactoryBuilder#allowOutOfTransactionUpdateOperations(boolean) + * + * @since 5.2 + */ + @Unsafe + String ALLOW_UPDATE_OUTSIDE_TRANSACTION = "hibernate.allow_update_outside_transaction"; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Unsafe.java b/hibernate-core/src/main/java/org/hibernate/cfg/Unsafe.java new file mode 100644 index 0000000000..6219e6b9c7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Unsafe.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.cfg; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + * Denotes that a setting is considered unsafe. Generally these are settings + * added for temporary use during porting of applications. Unsafe settings + * are largely considered unsupported. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Unsafe { +} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java index ebf99d1182..6f628226b4 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java @@ -109,6 +109,12 @@ public class AsciiDocWriter { if ( lifecycleDetails.isDeprecated() ) { writer.write( "WARNING: *_This setting is considered deprecated_*\n\n" ); } + if ( settingDescriptor.isUnsafe() ) { + writer.write( "WARNING: *_This setting is considered unsafe_*\n\n" ); + } + if ( settingDescriptor.isCompatibility() ) { + writer.write( "INFO: *_This setting manages a certain backwards compatibility_*\n\n" ); + } if ( lifecycleDetails.getSince() != null ) { writer.write( "*_Since:_* _" + lifecycleDetails.getSince() + "_\n\n" ); diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingDescriptor.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingDescriptor.java index 754512bc28..03ed313088 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingDescriptor.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingDescriptor.java @@ -18,6 +18,8 @@ public class SettingDescriptor { private final String defaultValue; private final String apiNote; private final LifecycleDetails lifecycleDetails; + private final boolean unsafe; + private final boolean compatibility; public SettingDescriptor( String name, @@ -27,6 +29,8 @@ public class SettingDescriptor { String comment, String defaultValue, String apiNote, + boolean unsafe, + boolean compatibility, LifecycleDetails lifecycleDetails) { this.name = name; this.settingsClassName = settingsClassName; @@ -35,6 +39,8 @@ public class SettingDescriptor { this.publishedJavadocLink = publishedJavadocLink; this.defaultValue = defaultValue; this.apiNote = apiNote; + this.unsafe = unsafe; + this.compatibility = compatibility; this.lifecycleDetails = lifecycleDetails; } @@ -48,7 +54,9 @@ public class SettingDescriptor { String apiNote, String since, boolean deprecated, - boolean incubating) { + boolean incubating, + boolean unsafe, + boolean compatibility) { this( name, settingsClassName, @@ -56,6 +64,8 @@ public class SettingDescriptor { publishedJavadocLink, comment, defaultValue, apiNote, + unsafe, + compatibility, new LifecycleDetails( since, deprecated, incubating ) ); } @@ -100,6 +110,14 @@ public class SettingDescriptor { return settingFieldName; } + public boolean isUnsafe() { + return unsafe; + } + + public boolean isCompatibility() { + return compatibility; + } + public LifecycleDetails getLifecycleDetails() { return lifecycleDetails; } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingWorkingDetails.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingWorkingDetails.java index d6dbd0b277..6fad3071c6 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingWorkingDetails.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/SettingWorkingDetails.java @@ -23,6 +23,8 @@ public class SettingWorkingDetails { private String since; private boolean deprecated; private boolean incubating; + private boolean unsafe; + private boolean compatibility; private List relatedSettingNames; public SettingWorkingDetails( @@ -92,6 +94,22 @@ public class SettingWorkingDetails { this.incubating = incubating; } + public boolean isUnsafe() { + return unsafe; + } + + public void setUnsafe(boolean unsafe) { + this.unsafe = unsafe; + } + + public boolean isCompatibility() { + return compatibility; + } + + public void setCompatibility(boolean compatibility) { + this.compatibility = compatibility; + } + public List getRelatedSettingNames() { return relatedSettingNames; } @@ -134,7 +152,9 @@ public class SettingWorkingDetails { apiNote, since, deprecated, - incubating + incubating, + unsafe, + compatibility ); } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/Utils.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/Utils.java index 61a7994b7c..b6e4ce8fcd 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/Utils.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/Utils.java @@ -14,6 +14,8 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import org.jsoup.nodes.Element; + /** * @author Steve Ebersole */ @@ -39,4 +41,39 @@ public class Utils { } ); return map; } + + public static boolean containsHref(Element fieldJavadocElement, String target) { + final String cssQuery = "a[href$=" + target + "]"; + final Element incubatingMarkerElement = fieldJavadocElement.selectFirst( cssQuery ); + return incubatingMarkerElement != null; + + } + + public static boolean interpretIncubation(Element fieldJavadocElement) { + return containsHref( fieldJavadocElement, "Incubating.html" ); + } + + public static boolean interpretUnsafe(Element fieldJavadocElement) { + return containsHref( fieldJavadocElement, "Unsafe.html" ); + } + + public static boolean interpretCompatibility(Element fieldJavadocElement) { + return containsHref( fieldJavadocElement, "Compatibility.html" ); + } + + public static boolean interpretDeprecation(Element fieldJavadocElement) { + // A setting is considered deprecated with either `@Deprecated` + final Element deprecationDiv = fieldJavadocElement.selectFirst( ".deprecationBlock" ); + // presence of this

    indicates the member is deprecated + if ( deprecationDiv != null ) { + return true; + } + + // or `@Remove` + if ( containsHref( fieldJavadocElement, "Remove.html" ) ) { + return true; + } + + return false; + } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk11/SettingsCollector.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk11/SettingsCollector.java index 0115a2c06a..9f1e5dc58a 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk11/SettingsCollector.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk11/SettingsCollector.java @@ -23,6 +23,10 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import static org.hibernate.orm.properties.Utils.interpretCompatibility; +import static org.hibernate.orm.properties.Utils.interpretDeprecation; +import static org.hibernate.orm.properties.Utils.interpretIncubation; +import static org.hibernate.orm.properties.Utils.interpretUnsafe; import static org.hibernate.orm.properties.Utils.packagePrefix; import static org.hibernate.orm.properties.Utils.withoutPackagePrefix; @@ -195,6 +199,8 @@ public class SettingsCollector { settingDetails.setIncubating( interpretIncubation( fieldJavadocElement ) ); settingDetails.setDeprecated( interpretDeprecation( fieldJavadocElement ) ); + settingDetails.setUnsafe( interpretUnsafe( fieldJavadocElement ) ); + settingDetails.setCompatibility( interpretCompatibility( fieldJavadocElement ) ); } private static SettingsDocSection findMatchingDocSection( @@ -245,28 +251,6 @@ public class SettingsCollector { } } - private static boolean interpretIncubation(Element fieldJavadocElement) { - final Element incubatingMarkerElement = fieldJavadocElement.selectFirst( "[href*=.Incubating.html]" ); - return incubatingMarkerElement != null; - } - - private static boolean interpretDeprecation(Element fieldJavadocElement) { - // A setting is considered deprecated with either `@Deprecated` - final Element deprecationDiv = fieldJavadocElement.selectFirst( ".deprecationBlock" ); - // presence of this
    indicates the member is deprecated - if ( deprecationDiv != null ) { - return true; - } - - // or `@Remove` - final Element removeMarkerElement = fieldJavadocElement.selectFirst( "[href*=.Remove.html]" ); - if ( removeMarkerElement != null ) { - return true; - } - - return false; - } - private static Elements cleanupFieldJavadocElement( Element fieldJavadocElement, String className, diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk17/SettingsCollector.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk17/SettingsCollector.java index e5bacf1cd1..b9aea25d99 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk17/SettingsCollector.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/jdk17/SettingsCollector.java @@ -24,6 +24,10 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import static org.hibernate.orm.properties.Utils.interpretCompatibility; +import static org.hibernate.orm.properties.Utils.interpretDeprecation; +import static org.hibernate.orm.properties.Utils.interpretIncubation; +import static org.hibernate.orm.properties.Utils.interpretUnsafe; import static org.hibernate.orm.properties.jdk17.JavadocToAsciidocConverter.convertFieldJavadocHtmlToAsciidoc; /** @@ -160,6 +164,8 @@ public class SettingsCollector { ); settingDetails.setIncubating( interpretIncubation( fieldJavadocElement ) ); settingDetails.setDeprecated( interpretDeprecation( fieldJavadocElement ) ); + settingDetails.setUnsafe( interpretUnsafe( fieldJavadocElement ) ); + settingDetails.setCompatibility( interpretCompatibility( fieldJavadocElement ) ); } public static Document loadConstants(File javadocDirectory) { @@ -244,27 +250,4 @@ public class SettingsCollector { noteConsumer.consumeNote( dtNode.text().trim(), ddNode.text().trim() ); } } - - private static boolean interpretIncubation(Element fieldJavadocElement) { - final Element incubatingMarkerElement = fieldJavadocElement.selectFirst( "[href*=.Incubating.html]" ); - return incubatingMarkerElement != null; - } - - private static boolean interpretDeprecation(Element fieldJavadocElement) { - // A setting is considered deprecated with either `@Deprecated` - final Element deprecationDiv = fieldJavadocElement.selectFirst( ".deprecation-block" ); - // presence of this
    indicates the member is deprecated - if ( deprecationDiv != null ) { - return true; - } - - // or `@Remove` - final Element removeMarkerElement = fieldJavadocElement.selectFirst( "[href*=.Remove.html]" ); - if ( removeMarkerElement != null ) { - return true; - } - - return false; - } - }