This guide discusses the Hibernate feature of generating non-identifier values. Jakarta Persistence allows for the generation of identifier values. Beyond generation of identifier values, Hibernate allows for the generation of other types of values. At a high level Hibernate supports both in-database and in-memory generation. In-database refers to generation strategy where the value is actually generated in the database as part of an INSERT/UPDATE execution, and must be re-read by SELECT afterwards to access the generated value. On the other hand, in-memory refers to strategies where the value is generated in memory and then passed along to the database via the INSERT/UPDATE statement.

Legacy support (in-database generation)

Historically applications would have to manually refresh the state of any entities that included such generated values to account for these generated values. Starting in version 3.2, however, Hibernate began allowing the application to mark attributes as generated, which allows the application to delegate this responsibility to Hibernate. When Hibernate issues an SQL INSERT or UPDATE for an entity that has defined in-database value generation, it immediately issues a select afterwards to retrieve the generated values.

To mark an attribute as generated, applications used @Generated:

Example 1. Legacy Syntax
@Entity
public class Document {
    ...
    @Generated
    private Date created;
    @Generated(event={INSERT,UPDATE})
    private Date lastModified;
}

The assumption above is that the values of the 2 attributes are generated during the execution of the SQL INSERT/UPDATE statements. @Generated tells Hibernate to read those values back after the INSERT/UPDATE execution.

@ValueGenerationType meta-annotation

Version 4.3 expands on that support by adding a new approach to declaring generated attributes and even custom generators via the @ValueGenerationType meta-annotation, which is used to annotate other annotations that describe generated attributes. For discussion purposes, I here copy all the contracts used in value generation:

@Target( value = ElementType.ANNOTATION_TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface ValueGenerationType {
    /**
     * The type of value generation associated with the annotated value generator
     * annotation type. The referenced generation type must be parameterized with
     * the type of the given generator annotation.
     *
     * @return the value generation type
     */
    Class<? extends AnnotationValueGeneration<?>> generatedBy();
}

public interface AnnotationValueGeneration<A extends Annotation>
        extends ValueGeneration {
    /**
     * Initializes this generation strategy for the given annotation instance.
     *
     * @param annotation an instance of the strategy's annotation type. Typically
     *     implementations will retrieve the annotation's attribute values and store
     *     them in fields.
     * @param propertyType the type of the property annotated with the generator
     *     annotation. Implementations may use the type to determine the right
     *     {@link ValueGenerator} to be applied.
     *
     * @throws HibernateException in case an error occurred during initialization,
     *     e.g. if an implementation can't create a value for the given property type.
     */
    void initialize(A annotation, Class<?> propertyType);
}

public interface ValueGeneration {
    /**
     * When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE)
     *
     * @return When the value is generated.
     */
    public 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
     *
     * @return The strategy for performing in-VM value generation
     */
    public ValueGenerator<?> getValueGenerator();

    /**
     * For values which are generated in the database (i.e,
     * {@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
     *
     * @return {@code true} indicates the column should be included in the SQL.
     */
    public boolean referenceColumnInSql();

    /**
     * For values which are generated in the database (i.e.,
     * {@link #getValueGenerator} == {@code null}), if the column will be
     * referenced in the SQL (i.e.,
     * {@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 ('?')
     *
     * @return The column value to be used in the SQL.
     */
    public String getDatabaseGeneratedReferencedColumnValue();
}

Ultimately, ValueGeneration is the thing seen by the Hibernate internals. But AnnotationValueGeneration gives the opportunity to configure the ValueGeneration via the annotation that named it. As mentioned, these contracts cater to both in-database and in-memory scenarios. Below we’ll look at some specific examples of usage in both in-database and in-memory scenarios as a means of illustration.

In-database Generation

@Generated has been retrofitted to use @ValueGenerationType. But @ValueGenerationType exposes more features than what @Generated currently supports. To leverage some of those features, you’d simply wire up a new generator annotation. For example, lets say we want the timestamps to be generated by calls to the standard ANSI SQL function current_timestamp (rather than triggers or DEFAULT values):

Example 2. In-database Custom Annotation Example
@ValueGenerationType(generatedBy = FunctionCreationValueGeneration.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionCreationTimestamp {
}

public class FunctionCreationValueGeneration
        implements AnnotationValueGeneration<FunctionCreationTimestamp>  {

    @Override
    public void initialize(FunctionCreationTimestamp annotation, Class<?> propertyType) {
    }

    public GenerationTiming getGenerationTiming() {
        // its creation...
        return GenerationTiming.INSERT;
    }

    public ValueGenerator<?> getValueGenerator() {
        // no in-memory generation
    }

    public boolean referenceColumnInSql() {
        return true;
    }

    public String getDatabaseGeneratedReferencedColumnValue() {
        return "current_timestamp";
    }
}

@Entity
public class ErrorReport {
    ...
    @FunctionCreationTimestamp
    private Date created;
}

In-memory Generation

Going back to the earlier Document example we can use some of the new pre-defined annotations to make the code a little cleaner and easier to understand:

Example 3. In-memory Generation Example
@Entity
public class Document {
    ...
    @CreationTimestamp
    private Date created;
    @UpdateTimestamp
    private Date lastModified;
}

Both @CreationTimestamp and @UpdateTimestamp perform in-memory generation of the timestamp (using the VM time).

Let’s also add an annotation for tracking the username who last modified the entity:

Example 4. Another In-memory Generation Example
@ValueGenerationType(generatedBy = ModifiedByValueGeneration.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModifiedBy {
}

public class ModifiedByValueGeneration
        implements AnnotationValueGeneration<ModifiedBy> {
    private final ValueGenerator<String> generator = new ValueGenerator<String>() {
        public String generateValue(Session session, Object owner) {
            // lets use a custom Service in the Hibernate ServiceRegistry to keep this
            // look up contextual and portable..
            UserService userService = ( (SessionImplementor) session ).getFactory()
                    .getServiceRegistry()
                    .getService( UserService.class );
            return userService.getCurrentUserName();
        }
    }

    @Override
    public void initialize(ModifiedBy annotation, Class<?> propertyType) {
    }

    public GenerationTiming getGenerationTiming() {
        return GenerationTiming.ALWAYS;
    }

    public ValueGenerator<?> getValueGenerator() {
        return generator;
    }

    public boolean referenceColumnInSql() {
        // n/a
        return false;
    }

    public String getDatabaseGeneratedReferencedColumnValue() {
        // n/a
        return null;
    }
}
@Entity
public class Document {
    ...
    @CreationTimestamp
    private Date created;
    @UpdateTimestamp
    private Date lastModified;
    @ModifiedBy
    private String lastModifiedBy;
}