hibernate/hibernate-orm · error · UnsupportedOperationException

Sets don't have indexes

Error message

Sets don't have indexes

What it means

PersistentCollection.getIndex(entry, i, persister) returns the collection index (list position or map key) that indexed collection persisters use when writing rows. PersistentSet implements it by throwing because a set is unordered and persisted as plain element rows with no index. Reaching this method means code or a persister that expects an indexed collection is operating on a set-shaped PersistentCollection — almost always a mapping/Java type mismatch.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentSet.java:411

		// assuming the user implements equals() properly, as required by the Set
		// contract!
		return oldValue == null && entry != null
			|| elemType.isDirty( oldValue, entry, getSession() );
	}

	@Override
	public boolean needsUpdating(Object entry, int i, Type elemType) {
		return false;
	}

	@Override
	public boolean isRowUpdatePossible() {
		return false;
	}

	@Override
	public Object getIndex(Object entry, int i, CollectionPersister persister) {
		throw new UnsupportedOperationException("Sets don't have indexes");
	}

	@Override
	public Object getElement(Object entry) {
		return entry;
	}

	@Override
	public Object getSnapshotElement(Object entry, int i) {
		throw new UnsupportedOperationException("Sets don't support updating by element");
	}

	@Override
	@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
	public boolean equals(Object other) {
		read();
		return set.equals( other );
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Align the Java type with the mapping: Set fields must use plain @OneToMany (no @OrderColumn/@MapKeyColumn) or <set>
  2. If the field is now a Set, delete the index metadata (@OrderColumn, @MapKeyColumn, <list-index>, <index>)
  3. Audit both annotations and any .hbm.xml for the same collection role after type changes
  4. In custom persister/collection code, never call getIndex on a PersistentSet — branch on collection kind first

Example fix

// before: Set field with indexed mapping
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@OrderColumn(name = "position")
private Set<Item> items = new HashSet<>();

// after: pick one — indexed List, or unordered Set without index
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Item> items = new HashSet<>();
Defensive patterns

Strategy: type-guard

Validate before calling

if (attributeJavaType == java.util.Set.class && mappingDeclaresIndex(collectionMapping)) {
    throw new IllegalStateException("Set attribute " + attributeName + " must not declare an index (@OrderColumn/@MapKeyColumn/<list>/<map>)");
}

Type guard

boolean canSupplyIndex(org.hibernate.collection.spi.PersistentCollection<?> collection) {
    // sets are unordered and never carry an index
    return !(collection instanceof org.hibernate.collection.spi.PersistentSet);
}

Try / catch

try {
    Object idx = persistentCollection.getIndex(entry, i, persister);
} catch (UnsupportedOperationException e) {
    throw new IllegalStateException("Indexed collection operation applied to a set-shaped collection; fix the mapping", e);
}

Prevention

When it happens

Trigger: A mapping declares an index (@OrderColumn/@ListIndex/@MapKeyColumn, <list>/<map> with <index>/<list-index>) but the Java attribute is a java.util.Set (PersistentSet); refactoring a field from List to Set while keeping the index metadata; custom persisters or metamodel/collection-copy code calling getIndex on a PersistentSet.

Common situations: Changing List to Set to fix duplicate-element bugs without updating annotations; .hbm.xml left with <list> after the field type changed; mixed annotation+hbm configuration of the same collection role; custom CollectionType implementations reusing set collections.

Related errors


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