hibernate/hibernate-orm · error · IllegalStateException

Constraint name was already set: %s

Error message

Constraint name was already set: %s

What it means

SqmConflictClause.conflictOnConstraintPaths(List) throws IllegalStateException when a constraint name was already set — the mirror case of conflictOnConstraint. The ON CONFLICT clause's conflict target must be declared exactly once, either by unique-constraint name or by the set of index column paths; once constraintName is non-null, adding paths is refused because the two forms would produce contradictory SQL (`ON CONFLICT ON CONSTRAINT x (cols...)` is invalid).

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/insert/SqmConflictClause.java:114

	public JpaConflictClause<T> conflictOnConstraintAttributes(@Nonnull SingularAttribute<T, ?>... attributes) {
		final ArrayList<SqmPath<?>> paths = new ArrayList<>( attributes.length );
		for ( SingularAttribute<T, ?> attribute : attributes ) {
			paths.add( insertStatement.getTarget().get( attribute ) );
		}
		return conflictOnConstraintPaths( paths );
	}

	@Nonnull
	@Override
	public SqmConflictClause<T> conflictOnConstraintPaths(@Nonnull Path<?>... paths) {
		return conflictOnConstraintPaths( Arrays.asList( paths ) );
	}

	@Nonnull
	@Override
	public SqmConflictClause<T> conflictOnConstraintPaths(@Nonnull List<? extends Path<?>> paths) {
		if ( constraintName != null ) {
			throw new IllegalStateException( "Constraint name was already set: " + constraintName );
		}
		//noinspection unchecked
		this.constraintPaths = (List<SqmPath<?>>) Collections.unmodifiableList( paths );
		return this;
	}

	@Nonnull
	@Override
	public List<SqmPath<?>> getConstraintPaths() {
		return constraintPaths == null
				? Collections.emptyList()
				: constraintPaths;
	}

	@Nonnull
	@Override
	public SqmConflictUpdateAction<T> createConflictUpdateAction() {
		return new SqmConflictUpdateAction<>( insertStatement );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove the conflictOnConstraint(name) call and keep only the paths form (or vice versa)
  2. Guard before adding paths: `if (clause.getConstraintName() == null) clause.conflictOnConstraintPaths(...)`
  3. Make the conflict-target style a single configuration choice (one enum/flag) in your query-building layer, mapped to exactly one API call
  4. Add a test that renders the final insert SQL once, catching double configuration early

Example fix

// before
insert.onConflict()
     .conflictOnConstraint("uk_person_ssn")
     .conflictOnConstraintPaths( root.get("ssn") ); // IllegalStateException

// after
insert.onConflict()
     .conflictOnConstraint( "uk_person_ssn" );
Defensive patterns

Strategy: validation

Validate before calling

if (clause.getConstraintName() == null) {
    clause.conflictOnConstraintPaths( paths );
} else {
    throw new IllegalStateException("Conflict target already declared by name: " + clause.getConstraintName());
}

Prevention

When it happens

Trigger: `insert.onConflict().conflictOnConstraint("uk_person_ssn").conflictOnConstraintPaths(root.get("ssn"))` — setting the name first, then paths; fluent chains that configure every available option; refactor leftovers where the columns call was added below an existing name call.

Common situations: Switching from named-constraint style to column-list style (common when moving to a database where constraint names differ between environments) without removing the old call; generated builders that emit both when both properties are configured; merging code from two upsert implementations.

Related errors


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