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
* is included in SQL {@code insert} and {@code update} statements. This
* is useful if the generated value is obtained by transforming the
* assigned property value as it is being written.
* is useful if:
* <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>
* Often used in combination with {@link SQLInsert}, {@link SQLUpdate},
* or {@link ColumnTransformer#write()}.

View File

@ -107,12 +107,13 @@ public interface Generator extends Serializable {
* @param session The session from which the request originates.
*
* @return {@code true} if the value is generated by the database as a side effect of
* the execution of an {@code insert} or {@code update} statement, or false if
* it is generated in Java code before the statement is executed via JDBC.
* the execution of an {@code insert} or {@code update} statement, or false if
* it is generated in Java code before the statement is executed via JDBC.
*
* @see #generatedOnExecution()
* @see BeforeExecutionGenerator
* @see OnExecutionGenerator
*
* @since 6.4
*/
default boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
@ -143,6 +144,19 @@ public interface Generator extends Serializable {
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() {
return !getEventTypes().isEmpty();
}

View File

@ -4,6 +4,7 @@
*/
package org.hibernate.generator.internal;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Generated;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -41,7 +42,10 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
public GeneratedGeneration(Generated annotation) {
eventTypes = fromArray( annotation.event() );
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
@ -51,7 +55,9 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override
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
@ -61,7 +67,9 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override
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
@ -71,13 +79,15 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
@Override
public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
if ( !writable ) {
if ( writable ) {
// When this is the identifier generator and writable is true, allow pre-assigned identifiers
final EntityPersister entityPersister = session.getEntityPersister( null, entity );
return entityPersister.getGenerator() != this
|| entityPersister.getIdentifier( entity, session ) == null;
}
else {
return true;
}
// When this is the identifier generator and writable is true, allow pre-assigned identifiers
final EntityPersister entityPersister = session.getEntityPersister( null, entity );
return entityPersister.getGenerator() != this || entityPersister.getIdentifier( entity, session ) == null;
}
@Override
@ -91,4 +101,10 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio
public boolean allowAssignedIdentifiers() {
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 {
generators[i] = generator;
final boolean noParameter = generatedWithNoParameter( generator );
if ( noParameter && !generator.allowAssignedIdentifiers() ) {
if ( !generator.allowMutation() ) {
propertyInsertability[i] = false;
propertyUpdateability[i] = false;
}
if ( generator.generatesOnInsert() ) {
if ( noParameter ) {
propertyInsertability[i] = false;
}
propertyInsertability[i] = !generatedWithNoParameter( generator );
if ( generator.generatedOnExecution() ) {
foundPostInsertGeneratedValues = true;
if ( generator instanceof BeforeExecutionGenerator ) {
@ -341,9 +338,7 @@ public class EntityMetamodel implements Serializable {
}
}
if ( generator.generatesOnUpdate() ) {
if ( noParameter ) {
propertyInsertability[i] = false;
}
propertyUpdateability[i] = !generatedWithNoParameter( generator );
if ( generator.generatedOnExecution() ) {
foundPostUpdateGeneratedValues = true;
if ( generator instanceof BeforeExecutionGenerator ) {