hibernate/hibernate-orm · error · SemanticException

Target type '%s' is not an entity

Error message

Target type '%s' is not an entity

What it means

When you set the target of a criteria UPDATE, SqmUpdateStatement.setTarget rejects roots whose model is an SqmPolymorphicRootDescriptor — Hibernate's synthetic type that stands for 'several entities at once' (e.g., a MappedSuperclass/abstract hierarchy root or an explicit polymorphic reference). There is no single entity to update in that case, so a SemanticException 'Target type ... is not an entity' is thrown.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/update/SqmUpdateStatement.java:272

		return versioned;
	}

	@Override
	public SqmUpdateStatement<T> versioned() {
		this.versioned = true;
		return this;
	}

	@Override
	public SqmUpdateStatement<T> versioned(boolean versioned) {
		this.versioned = versioned;
		return this;
	}

	@Override
	public void setTarget(@Nonnull JpaRoot<T> root) {
		if ( root.getModel() instanceof SqmPolymorphicRootDescriptor<?> ) {
			throw new SemanticException(
					String.format(
							"Target type '%s' is not an entity",
							root.getModel().getHibernateEntityName()
					)
			);
		}
		super.setTarget( root );
	}

	@Nonnull
	@Override
	public SqmUpdateStatement<T> where(@Nonnull Expression<Boolean> restriction) {
		setWhere( restriction );
		return this;
	}

	@Nonnull
	@Override

View on GitHub (pinned to fad1729dce)

Solutions

  1. Target a concrete @Entity subtype in createCriteriaUpdate(...) and in the root you pass to setTarget(...)
  2. If all subtypes need updating, iterate the concrete subtypes (from metamodel.getEntities() or your known list) and execute one update per entity
  3. Reconsider the design: polymorphic bulk updates across unioned entity tables are not expressible as a single SQL statement anyway

Example fix

// before
@MappedSuperclass abstract class BaseEvent { ... }
CriteriaUpdate<BaseEvent> u = cb.createCriteriaUpdate(BaseEvent.class); // polymorphic root
u.setTarget(u.from(BaseEvent.class)); // SemanticException: Target type is not an entity

// after — one update per concrete entity
for (Class<?> concrete : List.of(LoginEvent.class, PurchaseEvent.class)) {
    @SuppressWarnings("unchecked")
    CriteriaUpdate<BaseEvent> u = (CriteriaUpdate<BaseEvent>) cb.createCriteriaUpdate(concrete);
    Root<BaseEvent> r = (Root<BaseEvent>) u.from(concrete);
    u.set(r.get("archived"), true);
    em.createQuery(u).executeUpdate();
}
Defensive patterns

Strategy: validation

Validate before calling

if (AbstractSuperclass.class.isAssignableFrom(targetType) && !targetType.isAnnotationPresent(jakarta.persistence.Entity.class)) {
    throw new IllegalArgumentException("Update target must be a concrete @Entity, got " + targetType);
}

Type guard

static boolean isConcreteEntity(Class<?> c) { return c.isAnnotationPresent(jakarta.persistence.Entity.class); }

Try / catch

try { update.setTarget(root); } catch (SemanticException e) { if (e.getMessage().contains("not an entity")) { /* rebuild update against a concrete subtype */ } else throw e; }

Prevention

When it happens

Trigger: cb.createCriteriaUpdate(AbstractSuperclass.class) where AbstractSuperclass is a @MappedSuperclass (or polymorphic-typed root), then setTarget(root) — or building the update directly from a root of a non-entity abstract type.

Common situations: Inheritance hierarchies where services are typed against the abstract base; generic update helpers parameterized on a base class; code migrated from HQL updates on superclasses, which have their own restrictions.

Related errors


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