hibernate/hibernate-orm · error · HibernateException

Found shared references to a collection: {}

Error message

Found shared references to a collection: {}

What it means

During a single flush, Hibernate tracks every collection it has already reached (flushProcessingContext.markCollectionReached). Reaching the SAME collection instance a second time within one flush cycle means it is referenced from more than one place - typically two entities/properties pointing at one collection, or a circular reference between collections. The reached-check exists specifically to catch this user modeling error.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java:185

		final boolean isBytecodeEnhanced =
				persister.getOwnerEntityPersister()
						.getBytecodeEnhancementMetadata()
						.isEnhancedForLazyLoading();
		if ( isBytecodeEnhanced && !collection.wasInitialized() ) {
			// the class of the collection owner is enhanced for lazy loading,
			// and we found an un-initialized PersistentCollection, so skip it
			if ( CORE_LOGGER.isTraceEnabled() ) {
				CORE_LOGGER.skippingUninitializedBytecodeLazyCollection(
						collectionInfoString( persister, collection, collectionEntry.getCurrentKey(), session ) );
			}
			flushProcessingContext.markCollectionReached( collection );
			flushProcessingContext.markCollectionProcessed( collection );
		}
		// The reached status is just to detect any silly users
		// who set up circular or shared references between/to collections.
		else if ( flushProcessingContext.isCollectionReached( collection ) ) {
			// We've been here before
			throw new HibernateException( "Found shared references to a collection: " + type.getRole() );
		}
		else {
			flushProcessingContext.markCollectionReached( collection );
			logReachedCollection( collection, session, persister, collectionEntry );
			prepareCollectionForUpdate( collection, collectionEntry, factory, flushProcessingContext );
		}
	}

	private static void logReachedCollection(
			PersistentCollection<?> collection,
			SessionImplementor session,
			CollectionPersister persister,
			CollectionEntry collectionEntry) {
		if ( CORE_LOGGER.isTraceEnabled() ) {
			if ( collection.wasInitialized() ) {
				CORE_LOGGER.collectionFoundInitialized(
						collectionInfoString(
								persister,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Give each association its own collection instance; to sync bidirectional sides use element operations: parent.getChildren().add(c); c.setParent(parent);
  2. Audit code that assigns a collection obtained from a getter (a.get...() returns Hibernate's PersistentCollection - never hand it to another owner).
  3. Clear caches/pools that hold Hibernate-managed collections across requests or entities.
  4. Simplify the model if collections genuinely must be shared - map it as one association with multiple parents (many-to-many).

Example fix

// before - second assignment shares the same collection instance
invoiceA.setItems(invoiceB.getItems());
// after - copy elements into a fresh collection
invoiceA.setItems(new ArrayList<>(invoiceB.getItems()));
Defensive patterns

Strategy: validation

Validate before calling

// Guard: before wiring two associations, ensure they do not share one instance
static void assertNoSharedInstance(Collection<?> a, Collection<?> b, String role) {
    if (a != null && a == b) {
        throw new IllegalArgumentException("Shared collection instance for " + role
            + " - copy elements into separate collections");
    }
}

Type guard

static boolean isPersistentCollection(Object c) {
    return c instanceof org.hibernate.collection.spi.PersistentCollection;
}

Try / catch

catch (HibernateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Found shared references to a collection")) {
        // same collection reached twice in one flush; locate the duplicate reference and copy
        throw new MappingMisuseException(e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Two associations (two entities, or two collection-valued properties) referencing the same collection instance within the objects reachable at flush; bidirectional collection wiring that assigns one side's collection object to the other side; shared collections in cached or pooled domain objects. Thrown from processReachableCollection on the second visit.

Common situations: Synchronizing both sides of a bidirectional relation by copying the collection reference instead of adding elements; DTO round-trips that put one collection into two parents; session cache/second-level cache returning a shared collection; utility code that 'reuses' collections for memory reasons.

Related errors


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