java-document the use of @Immutable and @Mutability with AttributeConverters

This commit is contained in:
Gavin 2022-12-31 00:17:14 +01:00
parent efd6c8c6fd
commit 0d9a32a9df
3 changed files with 115 additions and 52 deletions

View File

@ -15,29 +15,55 @@ import static java.lang.annotation.ElementType.*;
/**
* Marks an entity, collection, or attribute of an entity as immutable. The absence of this
* annotation means the element is mutable.
* <ul>
* <li>
* Changes made in memory to the state of an immutable entity are never synchronized to
* the database. The changes are ignored, with no exception thrown. In a mapped inheritance
* hierarchy, {@code @Immutable} may be applied only to the root entity, and is inherited
* by entity subclasses. To make just one entity in the hierarchy immutable, annotate its
* attributes individually.
* </li>
* <li>
* An immutable collection may not be modified. A {@link org.hibernate.HibernateException}
* is thrown if an element is added to or removed from the collection.
* </li>
* <li>
* An immutable attribute is ignored by the dirty-checking process, and so the persistence
* context does not need to keep track of its state. This may help reduce memory allocation.
* </li>
* </ul>
*
* <h3>Immutable entities</h3>
* <p>
* This annotation may also be used to mark a Java type handled by a JPA
* Changes made in memory to the state of an immutable entity are never synchronized to
* the database. The changes are ignored, with no exception thrown.
* <p>
* An immutable entity need not be dirty-checked, and so Hibernate does not need to
* maintain a snapshot of its state. This may help reduce memory allocation.
* Note that it's also possible to obtain an entity in read-only mode in a given session,
* and this has similar benefits.
* <p>
* In a mapped inheritance hierarchy, {@code @Immutable} may be applied only to the root
* entity, and is inherited by entity subclasses. To make just one entity in the hierarchy
* immutable, annotate its attributes individually.
*
* <h3>Immutable basic-valued attributes</h3>
* <p>
* A mutable entity may have an immutable field or property.
* <p>
* An immutable attribute is ignored by the dirty-checking process, and so the persistence
* context does not need to keep track of its state. This may help reduce memory allocation.
*
* <h3>Immutable collections</h3>
* <p>
* An immutable collection may not be modified.
* <p>
* A {@link org.hibernate.HibernateException} is thrown if an element is added to or
* removed from the collection.
*
* <h3>Immutable for converters</h3>
* <p>
* {@code @Immutable} may also be used to mark a Java type handled by a JPA
* {@link jakarta.persistence.AttributeConverter} as immutable, circumventing the need to treat
* it as mutable.
* <p>
* Either:
* <ul>
* <li>annotate the Java type itself, or
* <li>annotate the {@code AttributeConverter} class.
* </ul>
* <p>
* This is not the same as marking the attribute {@code @Immutable}. A mutable attribute may
* have a type whose values are immutable.
*
* @author Emmanuel Bernard
*
* @see org.hibernate.Session#setDefaultReadOnly(boolean)
* @see org.hibernate.Session#setReadOnly(Object, boolean)
* @see org.hibernate.query.Query#setReadOnly(boolean)
*/
@Target({TYPE, METHOD, FIELD})
@Retention( RetentionPolicy.RUNTIME )

View File

@ -20,42 +20,72 @@ import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Specifies a {@link MutabilityPlan} for a basic value mapping.
* Specifies a {@link MutabilityPlan} for a some sort of basic value mapping.
*
* <h3>Mutability for basic-typed attributes</h3>
* <p>
* For basic-valued attributes, {@code @Mutability} specifies the mutability
* of the basic value type.
* <p>
* This is <em>not</em> the same as saying that the attribute itself is mutable
* or immutable. A mutable attribute may have a type whose values are immutable.
*
* <h3>Mutability for values belonging to collections</h3>
* <p>
* Even collection elements, indexes, keys, and values have mutability
* plans, and so this annotation may be applied to a collection-valued
* attribute:
* <ul>
* <li>
* When applied to a {@code Map}-valued attribute, describes
* the {@code Map} value. Use {@link MapKeyMutability} to
* describe the key instead.
* When applied to a {@code Map}-valued attribute, it describes
* the values of the map.
* Use {@link MapKeyMutability} to describe the keys of the map.
* </li>
* <li>
* When applied to a {@code List} of array-valued attribute,
* describes the element.
* </li>
* <li>
* When mapping an id-bag, describes the collection element.
* When mapping an id-bag, it describes the elements of the bag.
* Use {@link CollectionIdMutability} to describe the
* {@link CollectionId}.
* </li>
* <li>
* For other collection mappings, describes the elements.
* For {@code List}-valued attributes, or for any other collection
* mapping, it describes the elements of the collection.
* </li>
* <li>
* For discriminated association mappings ({@link Any} and
* {@link ManyToAny}), describes the discriminator value.
* When applied to an array-valued attribute, it describes the
* array element.
* </li>
* </ul>
* <p>
* Resolved as a {@link org.hibernate.resource.beans.spi.ManagedBean}.
* <p>
* See <a href="package-summary.html#basic-value-mapping">basic-value-mapping</a>
* for a high-level discussion of basic value mapping.
* Again, this is not the same as saying that the collection itself is
* mutable or immutable. One may add or remove immutable values to or
* from a mutable collection.
*
* @apiNote Valid on {@link ElementType#TYPE} in very limited cases.
* At the moment it is only supported on implementations of
* {@link jakarta.persistence.AttributeConverter}.
* <h3>Discriminated association mappings</h3>
* <p>
* For discriminated association mappings ({@link Any} or {@link ManyToAny}),
* this annotation describes the mutability of the discriminator value.
* <p>
* This is not likely to be useful.
*
* <h3>Mutability for converters</h3>
* <p>
* {@code @Mutability} may also be used to specify the mutability of a
* Java type handled by a JPA {@link jakarta.persistence.AttributeConverter},
* circumventing the need to treat it as mutable.
* <p>
* Either:
* <ul>
* <li>annotate the Java type itself, or
* <li>annotate the {@code AttributeConverter} class.
* </ul>
*
* @apiNote Except for the case of converters, this annotation is not
* usually applied to a {@linkplain ElementType#TYPE type}.
*
* @see Immutable
* @see MutabilityPlan
* @see <a href="package-summary.html#basic-value-mapping">Basic value type mappings</a>
* @see org.hibernate.type
*
* @since 6.0
*

View File

@ -11,8 +11,8 @@
* The JPA specification perfectly nails many aspects of the O/R persistence problem, but
* here we address some areas where it falls short.
*
* <h3 id="basic-value-mapping">Basic value type mappings</h3>
*
* <h3 id="basic-value-mapping">Basic types in JPA</h3>
* <p>
* A <em>basic type</em> handles the persistence of an attribute of an entity or embeddable
* object that is stored in exactly one database column.
* <p>
@ -82,6 +82,8 @@
* JPA does provide {@linkplain jakarta.persistence.AttributeConverter converters} as an
* extensibility mechanism, but its converters are only useful for classes which have an
* equivalent representation as one of the types listed above.
*
* <h3 id="basic-value-mapping">Basic value type mappings</h3>
* <p>
* By contrast, Hibernate has an embarrassingly rich set of abstractions for modelling
* basic types, which can be initially confusing.
@ -93,29 +95,34 @@
* Instead, a program should use either a "compositional" basic type, or in more extreme
* cases, a {@code UserType}.
* <ul>
* <li>
* <li><p>
* A basic type is a composition of a {@link org.hibernate.type.descriptor.java.JavaType}
* with a {@link org.hibernate.type.descriptor.jdbc.JdbcType}, and possibly a JPA
* {@link jakarta.persistence.AttributeConverter}, and the process of composition is
* usually somewhat implicit.
* <ol>
* <li>A converter may be selected using the JPA {@link jakarta.persistence.Convert}
* annotation.
* <li>A {@code JavaType} or {@code JdbcType} may be indicated <em>explicitly</em>
* <li><p>
* A converter may be selected using the JPA {@link jakarta.persistence.Convert}
* annotation, or it may be {@linkplain jakarta.persistence.Converter#autoApply()
* applied implicitly}.
* <li><p>
* A {@code JavaType} or {@code JdbcType} may be indicated <em>explicitly</em>
* using the following annotations:
* <ul>
* <li>{@link org.hibernate.annotations.JavaType}
* <li>{@link org.hibernate.annotations.JdbcType}
* <li>{@link org.hibernate.annotations.JdbcTypeCode}
* </ul>
* <li>But these annotation also influence the choice:
* <li><p>
* But these annotation also influence the choice:
* <ul>
* <li>{@link jakarta.persistence.Lob}
* <li>{@link jakarta.persistence.Enumerated}
* <li>{@link jakarta.persistence.Temporal}
* <li>{@link org.hibernate.annotations.Nationalized}
* </ul>
* <li>A compositional type mapping also comes with a
* <li><p>
* A compositional type mapping also comes with a
* {@link org.hibernate.type.descriptor.java.MutabilityPlan}, which is usually
* chosen by the {@code JavaType}, but which may be overridden using the
* {@link org.hibernate.annotations.Mutability} annotation.
@ -124,14 +131,14 @@
* Note that {@link org.hibernate.annotations.JavaType}, {@link org.hibernate.annotations.JdbcType},
* {@link org.hibernate.annotations.JdbcTypeCode} and {@link org.hibernate.annotations.Mutability}
* all come in specialized flavors for handling map keys, list indexes, and so on.
* <li>
* <li><p>
* Alternatively, a program may implement the {@link org.hibernate.usertype.UserType}
* interface and associate it with a field or property explicitly using the
* {@link org.hibernate.annotations.Type @Type} annotation, or implicitly using the
* {@link org.hibernate.annotations.TypeRegistration @TypeRegistration} annotation.
* <p>
* There are some specialized flavors of the {@code @Type} annotation too.
* </li>
* </li>
* </ul>
* <p>
* These two approaches cannot be used together. A {@code UserType} always takes precedence
@ -145,7 +152,7 @@
* discussion.
*
* <h3 id="second-level-cache">Second level cache</h3>
*
* <p>
* When we make a decision to store an entity in the second-level cache, we must decide
* much more than just whether "to cache or not to cache". Among other considerations:
* <ul>
@ -177,7 +184,7 @@
* advocated above.
*
* <h3 id="generated-values-natural-ids">Generated values</h3>
*
* <p>
* JPA supports {@linkplain jakarta.persistence.GeneratedValue generated} identifiers,
* that is, surrogate primary keys, with four useful built-in
* {@linkplain jakarta.persistence.GenerationType types} of id generation.
@ -212,7 +219,7 @@
* {@link org.hibernate.annotations.GeneratedColumn}.
*
* <h3 id="natural-ids">Natural ids</h3>
*
* <p>
* The use of surrogate keys is highly recommended, making it much easier to evolve
* a database schema over time. But every entity should also have a "natural" unique
* key: a subset of fields which, taken together, uniquely identify an instance of
@ -226,7 +233,7 @@
* by natural id is a very common thing to do, and so the cache can often be helpful.
*
* <h3 id="filters">Filters</h3>
*
* <p>
* Filters are an extremely powerful feature of Hibernate, allowing the definition of
* parameterized families of filtered "views" of the domain data. They're also easy
* to use, with the minor caveat that they require the developer to express filtering
@ -246,7 +253,7 @@
* particular session.
*
* <h3 id="dialect-specific-sql">Dialect-specific native SQL</h3>
*
* <p>
* Many annotations in this package allow the specification of native SQL expressions or
* even complete statements. For example:
* <ul>