{"record":{"id":"fc6241015107c49c","repo":"hibernate/hibernate-orm","slug":"a-field-marked-generated-writable-true-may-not","errorCode":null,"errorMessage":"A field marked '@Generated(writable=true)' may not specify explicit 'sql'","messagePattern":"A field marked '@Generated\\(writable=true\\)' may not specify explicit 'sql'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java","lineNumber":47,"sourceCode":"public class GeneratedGeneration implements OnExecutionGenerator {\n\n\tprivate final EnumSet<EventType> eventTypes;\n\tprivate final boolean writable;\n\tprivate final String[] sql;\n\tprivate Class<?> generatedType;\n\n\tpublic GeneratedGeneration(EnumSet<EventType> eventTypes) {\n\t\tthis.eventTypes = eventTypes;\n\t\twritable = false;\n\t\tsql = null;\n\t}\n\n\tpublic GeneratedGeneration(Generated annotation) {\n\t\teventTypes = fromArray( annotation.event() );\n\t\tsql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() };\n\t\twritable = annotation.writable();\n\t\tif ( sql != null && writable ) {\n\t\t\tthrow new AnnotationException( \"A field marked '@Generated(writable=true)' may not specify explicit 'sql'\" );\n\t\t}\n\t}\n\n\tpublic GeneratedGeneration(Generated annotation, GeneratorCreationContext context) {\n\t\tthis( annotation );\n\t\tgeneratedType = context.getType().getReturnedClass();\n\t}\n\n\t@Override\n\tpublic EnumSet<EventType> getEventTypes() {\n\t\treturn eventTypes;\n\t}\n\n\t@Override\n\tpublic Class<?> getGeneratedType() {\n\t\treturn generatedType;\n\t}\n","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java#L29-L65","documentation":"@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.","triggerScenarios":"Writing @Generated(event = ..., writable = true, sql = \"...\") on any attribute — the constructor throws as soon as the annotation is processed during mapping.","commonSituations":"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'.","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"],"exampleFix":"// before — rejected combination\n@Generated(event = EventType.ALWAYS, writable = true, sql = \"current_timestamp\")\nInstant updated;\n\n// after — writable value generated by the database; no explicit sql\n@Generated(event = EventType.ALWAYS, writable = true)\nInstant updated;","handlingStrategy":"validation","validationCode":"// Fail fast on @Generated(writable=true, sql=...), before booting\nstatic void checkGeneratedAnnotations(Class<?>... entities) {\n    for (Class<?> entity : entities) {\n        for (Field f : entity.getDeclaredFields()) {\n            Generated g = f.getAnnotation(Generated.class);\n            if (g != null && g.writable() && !g.sql().isEmpty()) {\n                throw new IllegalStateException(entity.getSimpleName() + \".\" + f.getName()\n                        + \": @Generated(writable=true) must not specify sql\");\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["hibernate","generated","annotation","validation","bootstrap"],"backgroundTag":"annotation-mapping-violation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}