{"record":{"id":"204a594ff7476467","repo":"hibernate/hibernate-orm","slug":"insert-conflict-clause-with-constraint-column-name","errorCode":null,"errorMessage":"Insert conflict clause with constraint column names is not supported","messagePattern":"Insert conflict clause with constraint column names is not supported","errorType":"exception","errorClass":"IllegalQueryOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java","lineNumber":1991,"sourceCode":"\n\t\tif ( hasAggregateFunctions( querySpec ) ) {\n\t\t\tif ( followOnStrategy == Locking.FollowOn.DISALLOW ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Locking with aggregate functions is not supported\" );\n\t\t\t}\n\t\t\telse if ( followOnStrategy == Locking.FollowOn.IGNORE ) {\n\t\t\t\treturn LockStrategy.NONE;\n\t\t\t}\n\t\t\tstrategy = LockStrategy.FOLLOW_ON;\n\t\t}\n\n\t\treturn strategy;\n\t}\n\n\tprotected void visitConflictClause(ConflictClause conflictClause) {\n\t\tif ( conflictClause != null ) {\n\t\t\t// By default, we only support do nothing with an optional constraint name\n\t\t\tif ( !conflictClause.getConstraintColumnNames().isEmpty() ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Insert conflict clause with constraint column names is not supported\" );\n\t\t\t}\n\t\t\tif ( conflictClause.isDoUpdate() ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Insert conflict do update clause is not supported\" );\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void visitStandardConflictClause(ConflictClause conflictClause) {\n\t\tif ( conflictClause == null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tclauseStack.push( Clause.CONFLICT );\n\t\tappendSql( \" on conflict\" );\n\t\tfinal String constraintName = conflictClause.getConstraintName();\n\t\tif ( constraintName != null ) {\n\t\t\tappendSql( \" on constraint \" );\n\t\t\tappendSql( constraintName );","sourceCodeStart":1973,"sourceCodeEnd":2009,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java#L1973-L2009","documentation":"visitConflictClause is the base implementation for INSERT conflict clauses, and by design it only supports DO NOTHING with an optional constraint name (dialects with richer upsert support override it). If the SQM conflict clause names constraint column names — 'on conflict (col1, col2)' — and the active dialect's translator did not override conflict handling, translation fails with IllegalQueryOperationException.","triggerScenarios":"HQL 'insert ... on conflict (col1, col2) do nothing' (constraint column names present) on a dialect whose translator still uses the base visitConflictClause — i.e., one without native or emulated upsert support.","commonSituations":"Using Hibernate 6.5+ HQL insert conflict clauses against older/less capable databases; enabling the feature in multi-dialect products (works on PostgreSQL, fails on a lesser-supported DB); community dialects not yet implementing conflict clause translation.","solutions":["Drop the constraint column list — plain 'on conflict do nothing' is the shape the base translator supports.","Use the dialect's native upsert through a native SQL query (e.g., INSERT ... ON CONFLICT / MERGE / ON DUPLICATE KEY UPDATE).","Handle the conflict in application logic: catch the constraint violation from a plain insert, or query-then-insert/update in a transaction."],"exampleFix":"// before — conflict column names on a dialect without upsert support\nsession.createQuery(\n    \"insert into Tag (id,name) values (:i,:n) on conflict (name) do nothing\").executeUpdate();\n\n// after — native upsert for that dialect, or plain insert + violation handling\nem.createNativeQuery(\"insert into tag (id,name) values (?,?) on conflict (name) do nothing\")\n  .setParameter(1, id).setParameter(2, name).executeUpdate();","handlingStrategy":"validation","validationCode":"// base translator supports only DO NOTHING without constraint columns:\n// gate the syntax before building HQL\nif (!dialectSupportsUpsertColumns(sessionFactory)) { // per-dialect capability flag you maintain\n    hqlConflictClause = \"on conflict do nothing\"; // drop column names on basic dialects\n}","typeGuard":null,"tryCatchPattern":"try { session.createQuery(insertHql).executeUpdate(); }\ncatch (org.hibernate.query.IllegalQueryOperationException e) {\n    if (e.getMessage().equals(\"Insert conflict clause with constraint column names is not supported\")) {\n        session.createQuery(insertHqlWithoutConflictColumns()).executeUpdate();\n    } else { throw e; }\n}","preventionTips":["Maintain a per-dialect capability map for upsert syntax and validate before issuing HQL.","Prefer the simplest portable form 'on conflict do nothing' for multi-database code.","Track Hibernate release notes — conflict-clause dialect support expands each minor."],"tags":["hibernate","orm","upsert","on-conflict","insert","dialect-capability"],"backgroundTag":"upsert-conflict-clause-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}