hibernate/hibernate-orm · error · AnnotationException
A field marked '@Generated(writable=true)' may not specify e
Error message
A field marked '@Generated(writable=true)' may not specify explicit 'sql'
What it means
@Generated supports two modes: read-only generated values (writable=false, the default) which may override the generated SQL fragment via sql, and writable values (writable=true) which the application may also assign and which must not carry an explicit sql. The GeneratedGeneration constructor rejects the combination @Generated(writable=true, sql="...") at bootstrap with this AnnotationException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java:47
public class GeneratedGeneration implements OnExecutionGenerator {
private final EnumSet<EventType> eventTypes;
private final boolean writable;
private final String[] sql;
private Class<?> generatedType;
public GeneratedGeneration(EnumSet<EventType> eventTypes) {
this.eventTypes = eventTypes;
writable = false;
sql = null;
}
public GeneratedGeneration(Generated annotation) {
eventTypes = fromArray( annotation.event() );
sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() };
writable = annotation.writable();
if ( sql != null && writable ) {
throw new AnnotationException( "A field marked '@Generated(writable=true)' may not specify explicit 'sql'" );
}
}
public GeneratedGeneration(Generated annotation, GeneratorCreationContext context) {
this( annotation );
generatedType = context.getType().getReturnedClass();
}
@Override
public EnumSet<EventType> getEventTypes() {
return eventTypes;
}
@Override
public Class<?> getGeneratedType() {
return generatedType;
}
View on GitHub (pinned to fad1729dce)
Solutions
- Remove sql and keep writable=true — the value is produced by the database (trigger/default) and the app may still write the field
- If you need the custom SQL fragment, set writable = false (default) and keep sql
- If the goal is DDL-level generation (GENERATED ALWAYS AS / identity), use @GeneratedColumn instead of @Generated(sql=...)
- If you need both app-writability and DB-side SQL, move the SQL into a trigger and let @Generated(writable=true) read back the result
Example fix
// before — rejected combination @Generated(event = EventType.ALWAYS, writable = true, sql = "current_timestamp") Instant updated; // after — writable value generated by the database; no explicit sql @Generated(event = EventType.ALWAYS, writable = true) Instant updated;
Defensive patterns
Strategy: validation
Validate before calling
// Fail fast on @Generated(writable=true, sql=...), before booting
static void checkGeneratedAnnotations(Class<?>... entities) {
for (Class<?> entity : entities) {
for (Field f : entity.getDeclaredFields()) {
Generated g = f.getAnnotation(Generated.class);
if (g != null && g.writable() && !g.sql().isEmpty()) {
throw new IllegalStateException(entity.getSimpleName() + "." + f.getName()
+ ": @Generated(writable=true) must not specify sql");
}
}
}
} Prevention
- Treat writable=true as 'the app may overwrite the DB-generated value' — it never needs an sql fragment
- Use @GeneratedColumn when the DDL itself must generate the column
- Add a mapping lint/CI check for annotation combinations Hibernate rejects at bootstrap
When it happens
Trigger: Writing @Generated(event = ..., writable = true, sql = "...") on any attribute — the constructor throws as soon as the annotation is processed during mapping.
Common situations: Copy-pasting examples that mix @GeneratedColumn (DDL-level generation) with @Generated(sql=...); wanting an app-writable trigger-generated column while also specifying the trigger SQL; misunderstanding the writable attribute introduced in Hibernate 6.x as 'writable by the SQL fragment'.
Related errors
- The {storageEngine} storage engine is not supported
- '@AttributeAccessor' annotation must specify a 'strategy'
- The specified package name cannot be null
- Unknown type of binding : <bindingRoot>
- Unable to resolve <jar-file/> reference - {}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/fc6241015107c49c.
Report an issue: GitHub.