After upgrading hibernate from v5 to v6, it is not possible to have columns with generated values that are not PK

Hi,

I have entity mapping

@Entity
@Table(name = "temp_file")
public class TempFile extends MyAppEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @GenericGenerator(name = "myapp_id", strategy = "MyAppIdGenerator")
    @GeneratedValue(generator = "myapp_id")
    @Generated(GenerationTime.INSERT)
    @Column(name = "uuid", insertable = false, nullable = false, unique = true, updatable = false)
    private String uuid;
    ...
}

This worked fine with Hibernate 5.6.15.Final, but now in Hibernate 6.5.2.Final it gives an error

Property 'TempFile.uuid' is annotated @GeneratedValue but is not part of an identifier

Does this mean that Hibernate 6.5.2.Final does not allow generated values beside PK?

Hi, the @GeneratedValue annotation must only be used for identifiers. Also, @GenericGenerator is deprecated, so I suggest using the new @ValueGenerationType meta-annotation to define a custom generation strategy that uses your generator. For example:

@ValueGenerationType( generatedBy = MyAppIdGenerator.class )
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface GeneratedUuidValue {
	GenerationTiming timing();
}

[...]

@GeneratedUuidValue
@Column(name = "uuid", insertable = false, nullable = false, unique = true, updatable = false)
private String uuid;

Find out more in our user guide.

Thanks for the quick answer.
Isn’t GenerationTiming deprecated as well? I should probably use EventType?

Also, since our MyAppIdGenerator implements org.hibernate.id.IdentifierGenerator, it now throws error
Generator class 'MyAppIdGenerator' implements 'IdentifierGenerator' and may not be used with '@ValueGenerationType'

Isn’t GenerationTiming deprecated as well?

You can customize the annotation members as needed, that one happened to already be in the example I took from but you could put your own values (or none if you don’t need any configuration).

You only need your generator to implement BeforeExecutionGenerator with timing EventType.INSERT and it should work with both identifiers and normal values. IdentifierGenerator is a classic extension point from the early Hibernate days but it’s now superfluous (see also its javadoc).

Thanks for the answer, I managed to implement it easily. It’s just EventType that’s substitution for GenerationTiming.
Also, I figured out from the documentation that generator needs to implement BeforeExecutionGenerator.

The trickiest part is about adapting queries for new SQM. E.g.

(COALESCE(:fromTime, '') = '' OR al.createdAt >= :fromTime)

where both al.createdAt and parameter :fromTime are java.time.LocalDateTime, but it’s not working anymore, saying:

org.hibernate.query.SemanticException: Literal type 'class java.lang.String' did not match domain type 'java.time.LocalDateTime' nor converted type 'java.util.Date'

and that’s due to SQM I guess, and it’s hard to find a (proper) solution for such cases (without explicit type casting).

The trickiest part is about adapting queries for new SQM. E.g.

(COALESCE(:fromTime, '') = '' OR al.createdAt >= :fromTime)

Why are you using coalesce(:fromTime, '') = '' here? That is not valid Hibernate syntax. If you know your parameter is going to be null, just don’t include the restriction in the query with i.e. dynamic Criteria APIs.

Anyway, if you want more specific help with this please create a new topic as it’s unrelated with the generator discussion.

1 Like

I’m using

coalesce(:fromTime, '') = ''

because

(:fromTime) is null

produces an error (in both v5 and v6): could not determine data type of parameter.

Anyway, I’m going to open another ticket for this one.
Thanks for helping!

Seems that I need to decorate the annotation with @IdGeneratorType in order to use it for PK field, cause having as you proposed

@ValueGenerationType( generatedBy = MyAppIdGenerator.class )
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface GeneratedUuidValue {
	GenerationTiming timing();
}

throws an error on startup: “Property ‘id’ is annotated ‘interface GeneratedUuidValue’ which is not an ‘@IdGeneratorType’”.

So, should I:

  1. decorate the annotation with both @IdGeneratorType and @ValueGeneratorType
@IdGenerationType( MyAppIdGenerator.class )
@ValueGenerationType( generatedBy = MyAppIdGenerator.class )
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface GeneratedUuidValue {
	GenerationTiming timing();
}
  1. decorate the annotation with only @IdGenerationType
@IdGenerationType( MyAppIdGenerator.class )
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface GeneratedUuidValue {
	GenerationTiming timing();
}
  1. have different annotations for id and other generated fields
  2. something else
    ?

You can surely annotate with both @IdGeneratorType and @ValueGenerationType if you want to use the same annotation for both identifiers and normal values, see e.g. Hibernate’s @Generated annotation.

Thanks for the quick answer.
I’ve checked @Generated annotation, and it has both @IdGenerationType and @ValueGenerationType, indeed (both using same generator).

Although, it seems that having only @IdGenerationType is enough for both, IDs and regular (generated) values.