clarify some javadoc for @Immutable and optimistic locking stuff

This commit is contained in:
Gavin 2022-12-04 12:13:44 +01:00 committed by Gavin King
parent 3c535c544f
commit bdc67f81b1
4 changed files with 38 additions and 24 deletions

View File

@ -13,13 +13,15 @@ import java.lang.annotation.RetentionPolicy;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
/** /**
* Marks an entity, collection, or attribute as immutable. The absence of this annotation * Marks an entity, collection, or attribute of an entity as immutable. The absence of this
* means the element is mutable. * annotation means the element is mutable.
* <ul> * <ul>
* <li> * <li>
* Changes made in memory to the state of an immutable entity are never synchronized to * 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 * 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. * 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>
* <li> * <li>
* An immutable collection may not be modified. A {@link org.hibernate.HibernateException} * An immutable collection may not be modified. A {@link org.hibernate.HibernateException}

View File

@ -12,11 +12,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* Specifies whether updating the annotated attribute should trigger an increment * Specifies whether mutating the annotated attribute should trigger an increment
* to the {@link jakarta.persistence.Version version} of the entity instance. * to the {@link jakarta.persistence.Version version} of the entity instance. Or,
* if {@link OptimisticLockType#ALL} or {@link OptimisticLockType#DIRTY} are used,
* specifies whether the attribute should be included or excluded from the list of
* checked attributes.
* <p> * <p>
* If this annotation is not present, updating an attribute does cause the version * If this annotation is not present, mutating an attribute <em>does</em> cause the
* to be incremented. * version to be incremented.
* *
* @author Logi Ragnarsson * @author Logi Ragnarsson
*/ */

View File

@ -14,7 +14,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specifies how optimistic lock checking works for the annotated entity. * Specifies how optimistic lock checking works for the annotated entity.
* We may detect that an optimistic lock has failed by checking either: * Optimistic lock checking may detect that an optimistic lock has failed,
* and that the transaction should be aborted, by comparing either:
* <ul> * <ul>
* <li>the {@linkplain OptimisticLockType#VERSION version or timestamp}, * <li>the {@linkplain OptimisticLockType#VERSION version or timestamp},
* <li>the {@linkplain OptimisticLockType#DIRTY dirty fields} of the * <li>the {@linkplain OptimisticLockType#DIRTY dirty fields} of the
@ -23,14 +24,22 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* </ul> * </ul>
* An optimistic lock is usually checked by including a restriction in a * An optimistic lock is usually checked by including a restriction in a
* SQL {@code update} or {@code delete} statement. If the database reports * SQL {@code update} or {@code delete} statement. If the database reports
* that zero rows were updated, we may infer that another transaction has * that zero rows were updated, it is inferred that another transaction
* already updated or deleted the row, and report the {@linkplain * has already updated or deleted the row, and the failure of the optimistic
* jakarta.persistence.OptimisticLockException failure} of the optimistic * lock is reported via an {@link jakarta.persistence.OptimisticLockException}.
* lock.
* <p> * <p>
* In an inheritance hierarchy, this annotation may only be applied to the * In an inheritance hierarchy, this annotation may only be applied to the
* root entity, since the optimistic lock checking strategy is inherited * root entity, since the optimistic lock checking strategy is inherited
* by entity subclasses. * by entity subclasses.
* <p>
* To exclude a particular attribute from optimistic locking, annotate the
* attribute {@link OptimisticLock @OptimisticLock(excluded=true)}. Then:
* <ul>
* <li>changes to that attribute will never trigger a version increment, and
* <li>the attribute will not be included in the list of fields checked fields
* when {@link OptimisticLockType#ALL} or {@link OptimisticLockType#DIRTY}
* is used.
* </ul>
* *
* @author Steve Ebersole * @author Steve Ebersole
* *

View File

@ -381,8 +381,8 @@ public class PropertyBinder {
return property; return property;
} }
private void handleNaturalId(Property prop) { private void handleNaturalId(Property property) {
final NaturalId naturalId = property.getAnnotation(NaturalId.class); final NaturalId naturalId = this.property.getAnnotation(NaturalId.class);
if ( naturalId != null ) { if ( naturalId != null ) {
if ( !entityBinder.isRootEntity() ) { if ( !entityBinder.isRootEntity() ) {
throw new AnnotationException( "Property '" + qualify( holder.getPath(), name ) throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
@ -392,27 +392,27 @@ public class PropertyBinder {
if ( !naturalId.mutable() ) { if ( !naturalId.mutable() ) {
updatable = false; updatable = false;
} }
prop.setNaturalIdentifier( true ); property.setNaturalIdentifier( true );
} }
} }
private void inferOptimisticLocking(Property prop) { private void inferOptimisticLocking(Property property) {
// this is already handled for collections in CollectionBinder... // this is already handled for collections in CollectionBinder...
if ( value instanceof Collection ) { if ( value instanceof Collection ) {
prop.setOptimisticLocked( ((Collection) value).isOptimisticLocked() ); property.setOptimisticLocked( ((Collection) value).isOptimisticLocked() );
} }
else if ( property != null && property.isAnnotationPresent(OptimisticLock.class) ) { else if ( this.property != null && this.property.isAnnotationPresent(OptimisticLock.class) ) {
final OptimisticLock lockAnn = property.getAnnotation(OptimisticLock.class); final OptimisticLock optimisticLock = this.property.getAnnotation(OptimisticLock.class);
validateOptimisticLock(lockAnn); validateOptimisticLock( optimisticLock );
prop.setOptimisticLocked( !lockAnn.excluded() ); property.setOptimisticLocked( !optimisticLock.excluded() );
} }
else { else {
prop.setOptimisticLocked( !isToOneValue(value) || insertable ); // && updatable as well??? property.setOptimisticLocked( !isToOneValue(value) || insertable ); // && updatable as well???
} }
} }
private void validateOptimisticLock(OptimisticLock lockAnn) { private void validateOptimisticLock(OptimisticLock optimisticLock) {
if ( lockAnn.excluded() ) { if ( optimisticLock.excluded() ) {
if ( property.isAnnotationPresent(Version.class) ) { if ( property.isAnnotationPresent(Version.class) ) {
throw new AnnotationException("Property '" + qualify( holder.getPath(), name ) throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
+ "' is annotated '@OptimisticLock(excluded=true)' and '@Version'" ); + "' is annotated '@OptimisticLock(excluded=true)' and '@Version'" );