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 );
	}

	@Nonnull

View on GitHub (pinned to fad1729dce)

Solutions

  1. Target one concrete entity: `insert into CreditCard (amount) select ...`
  2. If several subtypes must receive rows, issue one INSERT per concrete entity type
  3. Validate the target before building the statement: `root.getModel().getPersistenceType() == PersistenceType.ENTITY` plus checking it is not a polymorphic virtual descriptor
  4. 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

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


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