hibernate/hibernate-orm · critical · MappingException

must be an BeforeExecutionGenerator

Error message

must be an BeforeExecutionGenerator

What it means

For an identified collection (idbag), AbstractCollectionPersister.createGenerator builds the identifier generator for the collection-row id and requires it to be a BeforeExecutionGenerator (value available before the INSERT executes). If generator.generatedOnExecution() is true — identity/native/trigger-style generation — Hibernate throws MappingException('must be an BeforeExecutionGenerator') (the message typo is acknowledged with a TODO in Hibernate's source).

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java:688

				source = (ManagedMappingType) namedPart.getPartMappingType();
			}
			throw new MappingException(
					String.format(
							Locale.ROOT,
							"Unable to resolve mapped-by path : (%s) %s",
							entityPersister.getEntityName(),
							mappedByProperty
					)
			);
		}
	}

	private BeforeExecutionGenerator createGenerator(RuntimeModelCreationContext context, IdentifierCollection collection) {
		final Generator generator =
				collection.getIdentifier()
						.createGenerator( context.getDialect(), null, null, context.getGeneratorSettings() );
		if ( generator.generatedOnExecution() ) {
			throw new MappingException("must be an BeforeExecutionGenerator"); //TODO fix message
		}
		return (BeforeExecutionGenerator) generator;
	}

	private boolean shouldUseShallowCacheLayout(CacheLayout collectionQueryCacheLayout, SessionFactoryOptions options) {
		final var queryCacheLayout =
				collectionQueryCacheLayout == null
						? options.getQueryCacheLayout()
						: collectionQueryCacheLayout;
		return queryCacheLayout == CacheLayout.SHALLOW
			|| queryCacheLayout == CacheLayout.AUTO && cacheAccessStrategy != null;
	}

	@Override
	public NavigableRole getNavigableRole() {
		return navigableRole;
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Switch the @CollectionId generator to a before-execution strategy: sequence, increment, uuid, guid, hilo, or a table generator
  2. If you cannot pre-generate ids, drop @CollectionId and use a plain bag (row identity via the key + element columns)
  3. Model the rows as an explicit @Entity with its own id when database identity assignment is a hard requirement

Example fix

// before
@CollectionId(
  columns = @Column(name = "line_id"),
  generator = "native", // identity-style -> MappingException
  type = Long.class)
private List<String> tags;

// after
@GenericGenerator(name = "seq", strategy = "sequence",
  parameters = @Parameter(name = "sequence_name", value = "tag_seq"))
@CollectionId(
  columns = @Column(name = "line_id"),
  generator = "seq", // before-execution -> OK
  type = Long.class)
private List<String> tags;
Defensive patterns

Strategy: validation

Validate before calling

// Startup scan: @CollectionId must not use an execute-time generator (identity/native)
for (Class<?> c : scannedEntityClasses) {
  for (java.lang.reflect.Field f : c.getDeclaredFields()) {
    org.hibernate.annotations.CollectionId cid =
        f.getAnnotation(org.hibernate.annotations.CollectionId.class);
    if (cid != null) {
      org.hibernate.annotations.GenericGenerator g = f.getAnnotation(org.hibernate.annotations.GenericGenerator.class);
      String strategy = g != null ? g.strategy() : cid.generator();
      if (strategy.equals("identity") || strategy.equals("native") || strategy.equals("trigger")) {
        throw new IllegalStateException("@CollectionId on " + f + " uses execute-time generator '" + strategy
            + "'; idbags need a before-execution generator (sequence/increment/uuid/uuid2/table/hilo)");
      }
    }
  }
}

Try / catch

try {
  sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (MappingException e) {
  if (e.getMessage() != null && e.getMessage().contains("must be an BeforeExecutionGenerator")) {
    throw new IllegalStateException("Collection id generator must be before-execution (sequence/increment/uuid), not identity/native", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: @CollectionId combined with a database-identity generator: @GenericGenerator(strategy="native") or "identity" on the @CollectionId, or hbm.xml <collection-id><generator class="native"/></collection-id>; a custom PostInsertGenerator-style generator registered for the collection id.

Common situations: Teams reuse their entity ID strategy ('native' for autoincrement PKs) on @CollectionId; migrating HBM idbags that worked on older dialects; MySQL/SQLServer users where identity is the default everywhere.

Related errors


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