{"record":{"id":"c10c46a4a7da39ca","repo":"hibernate/hibernate-orm","slug":"cteinsertstrategy-can-only-be-used-with-dialects-t","errorCode":null,"errorMessage":"CteInsertStrategy can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well","messagePattern":"CteInsertStrategy can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"critical","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java","lineNumber":114,"sourceCode":"\tprivate final SessionFactoryImplementor sessionFactory;\n\tprivate final CteTable entityCteTable;\n\n\tpublic CteInsertStrategy(\n\t\t\tEntityMappingType rootEntityType,\n\t\t\tRuntimeModelCreationContext runtimeModelCreationContext) {\n\t\tthis( rootEntityType.getEntityPersister(), runtimeModelCreationContext );\n\t}\n\n\tpublic CteInsertStrategy(\n\t\t\tEntityPersister rootDescriptor,\n\t\t\tRuntimeModelCreationContext runtimeModelCreationContext) {\n\t\tthis.rootDescriptor = rootDescriptor;\n\t\tthis.sessionFactory = runtimeModelCreationContext.getSessionFactory();\n\n\t\tfinal Dialect dialect = runtimeModelCreationContext.getDialect();\n\n\t\tif ( !dialect.supportsNonQueryWithCTE() ) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\tgetClass().getSimpleName() +\n\t\t\t\t\t\t\t\" can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well\"\n\t\t\t);\n\t\t}\n\n\t\tfinal PersistentClass persistentClass = runtimeModelCreationContext.getMetadata()\n\t\t\t\t.getEntityBinding( rootDescriptor.getEntityName() );\n\t\tfinal Identifier tableNameIdentifier;\n\t\tif ( persistentClass instanceof SingleTableSubclass ) {\n\t\t\t// In this case, the descriptor is a subclass of a single table inheritance.\n\t\t\t// To avoid name collisions, we suffix the table name with the subclass number\n\t\t\ttableNameIdentifier = new Identifier(\n\t\t\t\t\tpersistentClass.getTable().getNameIdentifier().getText() + persistentClass.getSubclassId(),\n\t\t\t\t\tpersistentClass.getTable().getNameIdentifier().isQuoted()\n\t\t\t);\n\t\t}\n\t\telse {\n\t\t\ttableNameIdentifier = persistentClass.getTable().getNameIdentifier();","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java#L96-L132","documentation":"CteInsertStrategy implements multi-table inserts by chaining data-modifying CTEs, which the dialect must support (Dialect.supportsNonQueryWithCTE()). Only PostgreSQL, CockroachDB, SQL Server, and DB2 override it to true — MySQL/MariaDB do not. Constructing this strategy on an unsupported dialect throws UnsupportedOperationException during SessionFactory bootstrap, failing application startup.","triggerScenarios":"Setting `hibernate.query.mutation_strategy=cte` (AvailableSettings.QUERY_MUTATION_STRATEGY) in persistence.xml/application.properties while running MySQL/MariaDB; a global baseline config applied to all environments including non-CTE databases.","commonSituations":"Copying the popular `hibernate.query.mutation_strategy=cte` snippet (recommended for insert...select on PostgreSQL) into a MySQL project; switching the test/prod DB from PostgreSQL to MySQL without pruning Hibernate properties; the setting appearing in shared config libraries.","solutions":["Remove `hibernate.query.mutation_strategy=cte` and let Hibernate pick the table-based strategy automatically","Keep the setting only in PostgreSQL/DB2/SQL Server/CockroachDB-specific configuration profiles","Guard programmatically: enable the CTE strategy only if sessionFactory dialect.supportsNonQueryWithCTE() is true"],"exampleFix":"# before (persistence.xml on MySQL)\n<property name=\"hibernate.query.mutation_strategy\" value=\"cte\"/>\n\n# after\n<!-- removed: let Hibernate choose the strategy per dialect -->","handlingStrategy":"validation","validationCode":"// Before forcing the strategy, check the dialect capability\nMap<String, Object> props = new HashMap<>();\nDialect dialect = (Dialect) Class.forName(dialectClassName)\n        .getDeclaredConstructor().newInstance();\nif (dialect.supportsNonQueryWithCTE()) {\n    props.put(AvailableSettings.QUERY_MUTATION_STRATEGY, \"cte\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return Persistence.createEntityManagerFactory(\"pu\", props);\n} catch (PersistenceException e) {\n    if (e.getCause() instanceof UnsupportedOperationException uoe\n            && uoe.getMessage().contains(\"support CTE\")) {\n        throw new ConfigurationError(\"Remove hibernate.query.mutation_strategy=cte \"\n            + \"— this database lacks data-modifying CTEs\", e);\n    }\n    throw e;\n}","preventionTips":["Only set hibernate.query.mutation_strategy=cte for PostgreSQL, DB2, SQL Server, CockroachDB","MySQL/MariaDB cannot run UPDATE/DELETE inside CTEs — never force this strategy there","Fail fast in CI by booting the SessionFactory against the production-like database"],"tags":["hibernate-6","dialect","cte","configuration","startup-failure","mysql"],"backgroundTag":"dialect-capability-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}