HHH-18815 add Generator.allowMutation()

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-11-05 21:02:41 +01:00
parent 731a5dbdd5
commit 51254568df
4 changed files with 49 additions and 20 deletions

View File

@ -97,8 +97,12 @@ public @interface Generated {
/** /**
* Determines if the value currently assigned to the annotated property * Determines if the value currently assigned to the annotated property
* is included in SQL {@code insert} and {@code update} statements. This * is included in SQL {@code insert} and {@code update} statements. This
* is useful if the generated value is obtained by transforming the * is useful if:
* assigned property value as it is being written. * <ul>
* <li>the generated value is obtained by transforming the assigned
* property value as it is being written, or
* <li>assigning a value disables generation of a value.
* </ul>
* <p> * <p>
* Often used in combination with {@link SQLInsert}, {@link SQLUpdate}, * Often used in combination with {@link SQLInsert}, {@link SQLUpdate},
* or {@link ColumnTransformer#write()}. * or {@link ColumnTransformer#write()}.

View File

@ -113,6 +113,7 @@ public interface Generator extends Serializable {
* @see #generatedOnExecution() * @see #generatedOnExecution()
* @see BeforeExecutionGenerator * @see BeforeExecutionGenerator
* @see OnExecutionGenerator * @see OnExecutionGenerator
*
* @since 6.4 * @since 6.4
*/ */
default boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { default boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
@ -143,6 +144,19 @@ public interface Generator extends Serializable {
return false; return false;
} }
/**
* Determine if this generator allows generated fields to be manually assigned a value on
* {@linkplain #getEventTypes events} which do <em>not</em> trigger value generation.
*
* @return {@code true} if this generator allows manually assigned values,
* {@code false} otherwise (default).
*
* @since 7.0
*/
default boolean allowMutation() {
return false;
}
default boolean generatesSometimes() { default boolean generatesSometimes() {
return !getEventTypes().isEmpty(); return !getEventTypes().isEmpty();
} }

View File

@ -4,6 +4,7 @@
*/ */
package org.hibernate.generator.internal; package org.hibernate.generator.internal;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Generated; import org.hibernate.annotations.Generated;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -41,7 +42,10 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
public GeneratedGeneration(Generated annotation) { public GeneratedGeneration(Generated annotation) {
eventTypes = fromArray( annotation.event() ); eventTypes = fromArray( annotation.event() );
sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() }; sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() };
writable = annotation.writable() || sql != null; writable = annotation.writable();
if ( sql != null && writable ) {
throw new AnnotationException( "A field marked '@Generated(writable=true)' may not specify explicit 'sql'" );
}
} }
@Override @Override
@ -51,7 +55,9 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override @Override
public boolean referenceColumnsInSql(Dialect dialect) { public boolean referenceColumnsInSql(Dialect dialect) {
return writable; // include the column in when the field is writable,
// or when there is an explicit SQL expression
return writable || sql != null;
} }
@Override @Override
@ -61,7 +67,9 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override @Override
public boolean writePropertyValue() { public boolean writePropertyValue() {
return writable && sql==null; // include a ? parameter when the field is writable,
// but there is no explicit SQL expression
return writable;
} }
@Override @Override
@ -71,13 +79,15 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override @Override
public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
if ( !writable ) { if ( writable ) {
return true;
}
// When this is the identifier generator and writable is true, allow pre-assigned identifiers // When this is the identifier generator and writable is true, allow pre-assigned identifiers
final EntityPersister entityPersister = session.getEntityPersister( null, entity ); final EntityPersister entityPersister = session.getEntityPersister( null, entity );
return entityPersister.getGenerator() != this || entityPersister.getIdentifier( entity, session ) == null; return entityPersister.getGenerator() != this
|| entityPersister.getIdentifier( entity, session ) == null;
}
else {
return true;
}
} }
@Override @Override
@ -91,4 +101,10 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
public boolean allowAssignedIdentifiers() { public boolean allowAssignedIdentifiers() {
return writable; return writable;
} }
@Override
public boolean allowMutation() {
// the user may specify @Immutable if mutation should be disallowed
return true;
}
} }

View File

@ -321,15 +321,12 @@ public class EntityMetamodel implements Serializable {
} }
else { else {
generators[i] = generator; generators[i] = generator;
final boolean noParameter = generatedWithNoParameter( generator ); if ( !generator.allowMutation() ) {
if ( noParameter && !generator.allowAssignedIdentifiers() ) {
propertyInsertability[i] = false; propertyInsertability[i] = false;
propertyUpdateability[i] = false; propertyUpdateability[i] = false;
} }
if ( generator.generatesOnInsert() ) { if ( generator.generatesOnInsert() ) {
if ( noParameter ) { propertyInsertability[i] = !generatedWithNoParameter( generator );
propertyInsertability[i] = false;
}
if ( generator.generatedOnExecution() ) { if ( generator.generatedOnExecution() ) {
foundPostInsertGeneratedValues = true; foundPostInsertGeneratedValues = true;
if ( generator instanceof BeforeExecutionGenerator ) { if ( generator instanceof BeforeExecutionGenerator ) {
@ -341,9 +338,7 @@ public class EntityMetamodel implements Serializable {
} }
} }
if ( generator.generatesOnUpdate() ) { if ( generator.generatesOnUpdate() ) {
if ( noParameter ) { propertyUpdateability[i] = !generatedWithNoParameter( generator );
propertyInsertability[i] = false;
}
if ( generator.generatedOnExecution() ) { if ( generator.generatedOnExecution() ) {
foundPostUpdateGeneratedValues = true; foundPostUpdateGeneratedValues = true;
if ( generator instanceof BeforeExecutionGenerator ) { if ( generator instanceof BeforeExecutionGenerator ) {