hibernate/hibernate-orm · error · IllegalQueryOperationException

Dialect does not support values lists for insert statements

Error message

Dialect does not support values lists for insert statements

What it means

visitValuesListStandard emits 'values (..), (..), ..' for inserts and first checks dialect.supportsValuesListForInsert(). If the statement has more than one values tuple and the dialect cannot consume a multi-row VALUES list, translation stops with IllegalQueryOperationException. Classic case: Oracle before 23ai does not support multi-row VALUES inserts.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:1803

	protected void renderMergeUpdateClause(List<Assignment> assignments, Predicate wherePredicate) {
		if ( wherePredicate != null ) {
			appendSql( " and " );
			clauseStack.push( Clause.WHERE );
			wherePredicate.accept( this );
			clauseStack.pop();
		}
		appendSql( " then update" );
		renderSetClause( assignments );
	}

	protected void visitValuesList(List<Values> valuesList) {
		visitValuesListStandard( valuesList );
	}

	protected final void visitValuesListStandard(List<Values> valuesList) {
		if ( valuesList.size() != 1 && !dialect.supportsValuesListForInsert() ) {
			throw new IllegalQueryOperationException( "Dialect does not support values lists for insert statements" );
		}
		appendSql("values");
		final Stack<Clause> clauseStack = getClauseStack();
		try {
			clauseStack.push( Clause.VALUES );
			for ( int i = 0; i < valuesList.size(); i++ ) {
				if ( i != 0 ) {
					appendSql( COMMA_SEPARATOR_CHAR );
				}
				appendSql( " (" );
				final List<Expression> expressions = valuesList.get( i ).getExpressions();
				for ( int j = 0; j < expressions.size(); j++ ) {
					if ( j != 0 ) {
						appendSql( COMMA_SEPARATOR_CHAR );
					}
					expressions.get( j ).accept( this );
				}
				appendSql( ')' );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Split the statement into one insert per row, or use EntityManager.persist() with JDBC batching enabled (hibernate.jdbc.batch_size) which does not need VALUES lists.
  2. On Oracle 23ai, make sure the dialect detects version >= 23 (set hibernate.dialect or jakarta.persistence.database-version) so supportsValuesListForInsert() is true.
  3. Use a native multi-row INSERT ALL / vendor-specific batch insert for bulk loads.

Example fix

// before — multi-row VALUES list on Oracle < 23c
session.createQuery(
    "insert into Person (id,name) values (:i1,:n1),(:i2,:n2)").executeUpdate();

// after — per-row inserts with JDBC batching (hibernate.jdbc.batch_size=50)
for (Person p : batch) { em.persist(p); }
Defensive patterns

Strategy: validation

Validate before calling

org.hibernate.dialect.Dialect d = sessionFactory.getJdbcServices().getDialect();
if (rows.size() > 1 && !d.supportsValuesListForInsert()) {
    // dialect (e.g., Oracle < 23c) cannot take multi-row VALUES
    rows.forEach(this::insertOne); // or use persist() with JDBC batching
    return;
}

Try / catch

try { session.createQuery(multiRowInsertHql).executeUpdate(); }
catch (org.hibernate.query.IllegalQueryOperationException e) {
    if (e.getMessage().equals("Dialect does not support values lists for insert statements")) {
        rows.forEach(this::insertOne);
    } else { throw e; }
}

Prevention

When it happens

Trigger: HQL 'insert into Entity (...) values (...), (...), ...' or JPA CriteriaInsert with multiple value tuples, executed on a dialect where Dialect.supportsValuesListForInsert() returns false (notably Oracle pre-23c).

Common situations: Batch-loading data via multi-row HQL inserts; the same code running on PostgreSQL in dev but Oracle in production; upgrading to Hibernate 6.4+ where HQL multi-row insert values were introduced; Oracle version misdetected so the dialect thinks it is < 23c.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/6cc5c13ae7e0c4da. Report an issue: GitHub.