hibernate/hibernate-orm · error · SemanticException
Target type '%s' is not an entity
Error message
Target type '%s' is not an entity
What it means
AbstractSqmInsertStatement.setTarget throws SemanticException when the target root's model is a SqmPolymorphicRootDescriptor — Hibernate's virtual entity type for a Java type (usually an interface or non-entity superclass) that matches multiple mapped entities. An INSERT needs one concrete table to write to; a polymorphic reference like `PaymentMethod` (implemented by CreditCard and BankTransfer) designates several tables, so Hibernate refuses it as an insert target.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/insert/AbstractSqmInsertStatement.java:124
// if ( insertionTargetPaths.get( i ).getJavaTypeDescriptor() != expression.getNodeJavaType() ) {
// throw new SemanticException(
// String.format(
// "Expected insert attribute type [%s] did not match Query selection type [%s] at selection index [%d]",
// insertionTargetPaths.get( i ).getJavaTypeDescriptor().getTypeName(),
// expression.getNodeJavaType().getTypeName(),
// i
// ),
// hqlString,
// null
// );
// }
}
}
@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 List<SqmPath<?>> getInsertionTargetPaths() {
return insertionTargetPaths == null
? Collections.emptyList()
: Collections.unmodifiableList( insertionTargetPaths );
}
@NonnullView on GitHub (pinned to fad1729dce)
Solutions
- Target one concrete entity: `insert into CreditCard (amount) select ...`
- If several subtypes must receive rows, issue one INSERT per concrete entity type
- Validate the target before building the statement: `root.getModel().getPersistenceType() == PersistenceType.ENTITY` plus checking it is not a polymorphic virtual descriptor
- In generic code, resolve the actual entity: use the concrete subclass or `sessionFactory.getMetamodel().entity(concreteClass)`
Example fix
// before
session.createQuery("insert into PaymentMethod (amount) select p.amount from OldPayment p").executeUpdate();
// after
session.createQuery("insert into CreditCard (amount) select p.amount from OldPayment p").executeUpdate(); Defensive patterns
Strategy: validation
Validate before calling
import jakarta.persistence.metamodel.Type;
var model = root.getModel();
if (model.getPersistenceType() != Type.PersistenceType.ENTITY
|| model instanceof org.hibernate.query.sqm.tree.spi.domain.SqmPolymorphicRootDescriptor) {
throw new IllegalArgumentException("Insert target must be a single concrete entity: " + model);
} Type guard
static boolean isConcreteEntityTarget(jakarta.persistence.metamodel.EntityType<?> t) {
return !(t instanceof org.hibernate.query.sqm.tree.spi.domain.SqmPolymorphicRootDescriptor<?>);
} Try / catch
try { insert.executeUpdate(); }
catch (org.hibernate.query.SemanticException e) {
if (e.getMessage() != null && e.getMessage().contains("is not an entity")) {
// re-target a concrete entity class and rebuild the statement
} else throw e;
} Prevention
- Insert only into concrete @Entity classes
- Do not reuse polymorphic roots (interfaces/superclasses of several entities) as DML targets
- In generic DAOs, resolve the entity name via the metamodel and assert it maps to one table
When it happens
Trigger: HQL `insert into PaymentMethod (amount) select ...` where PaymentMethod is an interface/superclass mapped polymorphically across entities; criteria `insert.setTarget( root )` where root was built from `metamodel.entity(SomeInterface.class)` and the metamodel resolved it to a polymorphic descriptor; `from MyInterface` style roots reused as insert targets.
Common situations: Domain models with interface-based polymorphism where the interface is what the code references everywhere; porting SELECT queries (which DO support polymorphic roots — they are split into per-entity queries) to INSERT statements and assuming the same support; code that derives the insert target type generically from a variable of a shared supertype.
Related errors
- Expected insert attribute count [%d] did not match Query sel
- Insert conflict 'do update' clause with constraint name is n
- NATIVE is not a legal field for extract()
- Inverse distribution function '%s' must specify 'WITHIN GROU
- Insert conflict 'do update' clause with constraint name is n
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/65d035937e94a6bd.
Report an issue: GitHub.