HHH-15663 add writable member to @Generated annotation
This is useful if you're using custom SQL, e.g. @SqlInsert. Also improve the Javadoc surrounding all this stuff.
This commit is contained in:
parent
aef9ab2425
commit
383ffa56eb
|
@ -53,4 +53,13 @@ public @interface Generated {
|
|||
* </ul>
|
||||
*/
|
||||
GenerationTime value();
|
||||
|
||||
/**
|
||||
* Determines if the column mapped by the annotated property is included in SQL
|
||||
* {@code INSERT} and {@code UPDATE} statements. By default, it is excluded.
|
||||
*
|
||||
* @return {@code true} if the mapped column should be included in SQL
|
||||
* {@code INSERT} and {@code UPDATE} statements.
|
||||
*/
|
||||
boolean writable() default false;
|
||||
}
|
||||
|
|
|
@ -16,10 +16,16 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL generated by
|
||||
* Hibernate when an entity or collection row is deleted from the database.
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL
|
||||
* generated by Hibernate when an entity or collection row is deleted from the
|
||||
* database.
|
||||
* <p>
|
||||
* The given {@link #sql SQL statement} must have exactly the number of JDBC
|
||||
* {@code ?} parameters that Hibernate expects, in the exact order Hibernate
|
||||
* expects. The primary key columns come before the version column if the
|
||||
* entity is versioned.
|
||||
*
|
||||
* @author L<EFBFBD>szl<EFBFBD> Benke
|
||||
* @author Laszlo Benke
|
||||
*/
|
||||
@Target({TYPE, FIELD, METHOD})
|
||||
@Retention(RUNTIME)
|
||||
|
|
|
@ -16,10 +16,26 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL generated by
|
||||
* Hibernate when an entity or collection row is inserted in the database.
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL
|
||||
* generated by Hibernate when an entity or collection row is inserted in the
|
||||
* database.
|
||||
* <p>
|
||||
* The given {@link #sql SQL statement} must have exactly the number of JDBC
|
||||
* {@code ?} parameters that Hibernate expects, that is, one for each column
|
||||
* mapped by the entity, in the exact order Hibernate expects. In particular,
|
||||
* the {@link jakarta.persistence.Id primary key} columns must come last.
|
||||
* <p>
|
||||
* If a column should <em>not</em> be written as part of the insert statement,
|
||||
* and has no corresponding JDBC parameter in the custom SQL, it must be mapped
|
||||
* using {@link jakarta.persistence.Column#insertable insertable=false}.
|
||||
* <p>
|
||||
* A custom SQL insert statement might transform the column values as they
|
||||
* are written. In this case, the state of the entity held in memory loses
|
||||
* synchronization with the database after the insert is executed unless
|
||||
* {@link Generated @Generated(value=INSERT, writable=true)} is specified,
|
||||
* forcing Hibernate to reread the state of the entity after each insert.
|
||||
*
|
||||
* @author L<EFBFBD>szl<EFBFBD> Benke
|
||||
* @author Laszlo Benke
|
||||
*/
|
||||
@Target({TYPE, FIELD, METHOD})
|
||||
@Retention(RUNTIME)
|
||||
|
|
|
@ -16,10 +16,29 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL generated by
|
||||
* Hibernate when an entity or collection row is updated in the database.
|
||||
* Specifies a custom SQL DML statement to be used in place of the default SQL
|
||||
* generated by Hibernate when an entity or collection row is updated in the
|
||||
* database.
|
||||
* <p>
|
||||
* The given {@link #sql SQL statement} must have exactly the number of JDBC
|
||||
* {@code ?} parameters that Hibernate expects, that is, one for each column
|
||||
* mapped by the entity, in the exact order Hibernate expects. In particular,
|
||||
* the {@link jakarta.persistence.Id primary key} columns come last unless
|
||||
* the entity is {@link jakarta.persistence.Version versioned}, in which case
|
||||
* there must be a second JDBC parameter for the version column, which comes
|
||||
* after the primary key.
|
||||
* <p>
|
||||
* If a column should <em>not</em> be written as part of the update statement,
|
||||
* and has no corresponding JDBC parameter in the custom SQL, it must be mapped
|
||||
* using {@link jakarta.persistence.Column#updatable() updatable=false}.
|
||||
* <p>
|
||||
* A custom SQL update statement might transform the column values as they
|
||||
* are written. In this case, the state of the entity held in memory loses
|
||||
* synchronization with the database after the update is executed unless
|
||||
* {@link Generated @Generated(value=ALWAYS, writable=true)} is specified,
|
||||
* forcing Hibernate to reread the state of the entity after each update.
|
||||
*
|
||||
* @author L<EFBFBD>szl<EFBFBD> Benke
|
||||
* @author Laszlo Benke
|
||||
*/
|
||||
@Target({TYPE, FIELD, METHOD})
|
||||
@Retention(RUNTIME)
|
||||
|
|
|
@ -390,7 +390,7 @@ public class PropertyBinder {
|
|||
return NoValueGeneration.INSTANCE;
|
||||
}
|
||||
|
||||
if ( valueGeneration.getValueGenerator() == null ) {
|
||||
if ( !valueGeneration.writeColumn() ) {
|
||||
// if we have an in-db generator, mark it as not insertable nor updatable
|
||||
insertable = false;
|
||||
updatable = false;
|
||||
|
|
|
@ -14,7 +14,8 @@ import org.hibernate.engine.spi.Mapping;
|
|||
import org.hibernate.internal.util.StringHelper;
|
||||
|
||||
/**
|
||||
* A mapping model object representing a unique key constraint on a relational database table.
|
||||
* A mapping model object representing a {@linkplain jakarta.persistence.UniqueConstraint unique key}
|
||||
* constraint on a relational database table.
|
||||
*
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
|
|
|
@ -39,7 +39,7 @@ public interface GeneratedValueResolver {
|
|||
// value generation which we'll circle back to as we convert write operations to
|
||||
// use the "runtime mapping" (`org.hibernate.metamodel.mapping`) model
|
||||
|
||||
if ( valueGeneration.getValueGenerator() == null ) {
|
||||
if ( valueGeneration.generatedByDatabase() ) {
|
||||
// in-db generation (column-default, function, etc)
|
||||
return new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition );
|
||||
}
|
||||
|
|
|
@ -2937,7 +2937,7 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
final ValueGeneration valueGeneration = attributeMapping.getValueGeneration();
|
||||
if ( valueGeneration.getGenerationTiming().includesUpdate()
|
||||
&& valueGeneration.getValueGenerator() == null
|
||||
&& valueGeneration.generatedByDatabase()
|
||||
&& valueGeneration.referenceColumnInSql() ) {
|
||||
update.addColumns(
|
||||
getPropertyColumnNames( index ),
|
||||
|
@ -3059,7 +3059,7 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
final ValueGeneration valueGeneration = attributeMapping.getValueGeneration();
|
||||
if ( valueGeneration.getGenerationTiming().includesInsert()
|
||||
&& valueGeneration.getValueGenerator() == null
|
||||
&& valueGeneration.generatedByDatabase()
|
||||
&& valueGeneration.referenceColumnInSql() ) {
|
||||
insert.addColumns(
|
||||
getPropertyColumnNames( index ),
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.annotations.Generated;
|
|||
public class GeneratedValueGeneration implements AnnotationValueGeneration<Generated> {
|
||||
|
||||
private GenerationTiming timing;
|
||||
private boolean writable;
|
||||
|
||||
public GeneratedValueGeneration() {
|
||||
}
|
||||
|
@ -27,7 +28,8 @@ public class GeneratedValueGeneration implements AnnotationValueGeneration<Gener
|
|||
|
||||
@Override
|
||||
public void initialize(Generated annotation, Class<?> propertyType) {
|
||||
this.timing = annotation.value().getEquivalent();
|
||||
timing = annotation.value().getEquivalent();
|
||||
writable = annotation.writable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,8 +45,7 @@ public class GeneratedValueGeneration implements AnnotationValueGeneration<Gener
|
|||
|
||||
@Override
|
||||
public boolean referenceColumnInSql() {
|
||||
// historically these columns are not referenced in the SQL
|
||||
return false;
|
||||
return writable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,48 +9,98 @@ package org.hibernate.tuple;
|
|||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Describes the generation of property values.
|
||||
* Describes the generation of values of a certain property of an entity. Property values might
|
||||
* be generated in Java, or by the database.
|
||||
*
|
||||
* @see org.hibernate.annotations.ValueGenerationType
|
||||
* @see org.hibernate.annotations.Generated
|
||||
* @see org.hibernate.annotations.GeneratorType
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ValueGeneration extends Serializable {
|
||||
/**
|
||||
* When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE)
|
||||
* Specifies that the property value is generated:
|
||||
* <ul>
|
||||
* <li>{@linkplain GenerationTiming#INSERT when the entity is inserted},
|
||||
* <li>{@linkplain GenerationTiming#ALWAYS whenever the entity is inserted or updated}, or
|
||||
* <li>{@linkplain GenerationTiming#NEVER never}.
|
||||
* </ul>
|
||||
*
|
||||
* @return When the value is generated.
|
||||
* @return The {@link GenerationTiming} specifying when the value is generated.
|
||||
*/
|
||||
GenerationTiming getGenerationTiming();
|
||||
|
||||
/**
|
||||
* Obtain the in-VM value generator.
|
||||
* <p/>
|
||||
* May return {@code null}. In fact for values that are generated "in the database" via execution of the
|
||||
* INSERT/UPDATE statement, the expectation is that {@code null} be returned here
|
||||
* Obtain the {@linkplain ValueGenerator Java value generator}, if the value is generated in
|
||||
* Java, or return {@code null} if the value is generated by the database.
|
||||
*
|
||||
* @return The strategy for performing in-VM value generation
|
||||
* @return The value generator
|
||||
*/
|
||||
ValueGenerator<?> getValueGenerator();
|
||||
|
||||
/**
|
||||
* For values which are generated in the database ({@link #getValueGenerator()} == {@code null}), should the
|
||||
* column be referenced in the INSERT / UPDATE SQL?
|
||||
* <p/>
|
||||
* This will be false most often to have a DDL-defined DEFAULT value be applied on INSERT
|
||||
* Determines if the column whose value is generated is included in the column list of the
|
||||
* SQL {@code insert} or {@code update} statement, in the case where the value is generated
|
||||
* by the database. For example, this method should return:
|
||||
* <ul>
|
||||
* <li>{@code true} if the value is generated by calling a SQL function like
|
||||
* {@code current_timestamp}, or
|
||||
* <li>{@code false} if the value is generated by a trigger,
|
||||
* by {@link org.hibernate.annotations.GeneratedColumn generated always as}, or
|
||||
* using a {@linkplain org.hibernate.annotations.ColumnDefault column default value}.
|
||||
* </ul>
|
||||
* If the value is generated in Java, this method is not called, and so for backward
|
||||
* compatibility with Hibernate 5 it is permitted to return any value. On the other hand,
|
||||
* when a property value is generated in Java, the column certainly must be included in the
|
||||
* column list, and so it's most correct for this method to return {@code true}!
|
||||
*
|
||||
* @return {@code true} indicates the column should be included in the SQL.
|
||||
* @return {@code true} if the column is included in the column list of the SQL statement.
|
||||
*/
|
||||
boolean referenceColumnInSql();
|
||||
|
||||
/**
|
||||
* For values which are generated in the database ({@link #getValueGenerator} == {@code null}), if the
|
||||
* column will be referenced in the SQL ({@link #referenceColumnInSql()} == {@code true}), what value should be
|
||||
* used in the SQL as the column value.
|
||||
* <p/>
|
||||
* Generally this will be a function call or a marker (DEFAULTS).
|
||||
* <p/>
|
||||
* NOTE : for in-VM generation, this will not be called and the column value will implicitly be a JDBC parameter ('?')
|
||||
* A SQL expression indicating how to calculate the generated value when the property value
|
||||
* is {@linkplain #generatedByDatabase() generated in the database} and the mapped column is
|
||||
* {@linkplain #referenceColumnInSql() included in the SQL statement}. The SQL expression
|
||||
* might be:
|
||||
* <ul>
|
||||
* <li>a function call like {@code current_timestamp} or {@code nextval('mysequence')}, or
|
||||
* <li>a syntactic marker like {@code default}.
|
||||
* </ul>
|
||||
* When the property value is generated in Java, this method is not called, and its value is
|
||||
* implicitly the string {@code "?"}, that is, a JDBC parameter to which the generated value
|
||||
* is bound.
|
||||
*
|
||||
* @return The column value to be used in the SQL.
|
||||
* @return The column value to be used in the generated SQL statement.
|
||||
*/
|
||||
String getDatabaseGeneratedReferencedColumnValue();
|
||||
|
||||
/**
|
||||
* Determines if the property value is generated in Java, or by the database.
|
||||
* <p>
|
||||
* This default implementation returns true if the {@linkplain #getValueGenerator() Java
|
||||
* value generator} is {@code null}.
|
||||
*
|
||||
* @return {@code true} if the value is generated by the database, or false if it is
|
||||
* generated in Java using a {@link ValueGenerator}.
|
||||
*/
|
||||
default boolean generatedByDatabase() {
|
||||
return getValueGenerator() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the property value is written to JDBC as the argument of a JDBC {@code ?}
|
||||
* parameter. This is the case when either:
|
||||
* <ul>
|
||||
* <li>the value is generated in Java, or
|
||||
* <li>{@link #referenceColumnInSql()} is {@code true} and
|
||||
* {@link #getDatabaseGeneratedReferencedColumnValue()} returns {@code null}.
|
||||
* </ul>
|
||||
*/
|
||||
default boolean writeColumn() {
|
||||
return !generatedByDatabase() // value generated in memory and then written as normal
|
||||
// current value of property of entity instance written completely as normal
|
||||
|| referenceColumnInSql() && getDatabaseGeneratedReferencedColumnValue()==null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -449,7 +449,7 @@ public class EntityMetamodel implements Serializable {
|
|||
final ValueGeneration valueGeneration = mappingProperty.getValueGenerationStrategy();
|
||||
if ( valueGeneration != null && valueGeneration.getGenerationTiming() != GenerationTiming.NEVER ) {
|
||||
// the property is generated in full. build the generation strategy pair.
|
||||
if ( valueGeneration.getValueGenerator() != null ) {
|
||||
if ( !valueGeneration.generatedByDatabase() ) {
|
||||
// in-memory generation
|
||||
return new GenerationStrategyPair(
|
||||
FullInMemoryValueGenerationStrategy.create( valueGeneration )
|
||||
|
|
Loading…
Reference in New Issue