hibernate/hibernate-orm · error · MappingException

Basic collection has element type '%s' which is not a known

Error message

Basic collection has element type '%s' which is not a known basic type (attribute is not annotated '@ElementCollection', '@OneToMany', or '@ManyToMany')

What it means

BasicCollectionJavaType models a List/Set treated as one basic value (e.g. an array-mapped column). Before choosing a JDBC type it must resolve the element type; if the element's JavaType is UnknownBasicJavaType, Hibernate has no mapping for it and bootstrap fails with this MappingException. The message itself hints the cause: the attribute probably needed @ElementCollection/@OneToMany/@ManyToMany semantics instead of basic-collection semantics.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java:76

	private final CollectionSemantics<C, E> semantics;
	private final JavaType<E> componentJavaType;

	public BasicCollectionJavaType(ParameterizedType type, JavaType<E> componentJavaType, CollectionSemantics<C, E> semantics) {
		super( type, new CollectionMutabilityPlan<>( componentJavaType, semantics ) );
		this.semantics = semantics;
		this.componentJavaType = componentJavaType;
	}

	@Override
	public JavaType<E> getElementJavaType() {
		return componentJavaType;
	}

	@Override
	public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
		if ( componentJavaType instanceof UnknownBasicJavaType ) {
			throw new MappingException("Basic collection has element type '"
					+ componentJavaType.getTypeName()
					+ "' which is not a known basic type"
					+ " (attribute is not annotated '@ElementCollection', '@OneToMany', or '@ManyToMany')");
		}
		// Always determine the recommended type to make sure this is a valid basic java type
		// (even though we only use this inside the if block, we want it to throw here if something wrong)
		final var recommendedComponentJdbcType = componentJavaType.getRecommendedJdbcType( indicators );
		final var typeConfiguration = indicators.getTypeConfiguration();
		return typeConfiguration.getJdbcTypeRegistry()
				.resolveTypeConstructorDescriptor(
						indicators.getPreferredSqlTypeCodeForArray( recommendedComponentJdbcType.getDefaultSqlTypeCode() ),
						typeConfiguration.getBasicTypeRegistry().resolve( componentJavaType, recommendedComponentJdbcType ),
						ColumnTypeInformation.EMPTY
				);
	}

	public CollectionSemantics<C, E> getSemantics() {
		return semantics;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add @ElementCollection to collections of basic or embeddable elements
  2. Use @OneToMany/@ManyToMany when the elements are entities
  3. Register a JavaType for the element class via @JavaTypeRegistration or MetadataBuilder.applyBasicType so it is no longer 'unknown'
  4. For custom value classes, add an AttributeConverter to a supported basic type (String, int, ...) which supplies the JDBC side implicitly

Example fix

// before
@Entity class Doc {
    @JdbcTypeCode(SqlTypes.ARRAY)
    @Column(name = "tags")
    List<String> tags = new ArrayList<>(); // element resolves to UnknownBasicJavaType -> MappingException
}

// after
@Entity class Doc {
    @ElementCollection
    @JdbcTypeCode(SqlTypes.ARRAY)
    @Column(name = "tags")
    List<String> tags = new ArrayList<>();
}
Defensive patterns

Strategy: validation

Validate before calling

static List<String> collectionFieldsMissingAnnotations(Class<?>... entities) {
    var missing = new ArrayList<String>();
    for (var e : entities)
        for (var f : e.getDeclaredFields())
            if (java.util.Collection.class.isAssignableFrom(f.getType())
                && !f.isAnnotationPresent(jakarta.persistence.ElementCollection.class)
                && !f.isAnnotationPresent(jakarta.persistence.OneToMany.class)
                && !f.isAnnotationPresent(jakarta.persistence.ManyToMany.class)
                && !f.isAnnotationPresent(jakarta.persistence.Transient.class))
                missing.add(e.getName() + "." + f.getName());
    return missing; // assert empty before buildSessionFactory()
}

Try / catch

try {
    sessionFactory = metadata.buildSessionFactory();
} catch (org.hibernate.MappingException e) {
    // message names the element type; fix the annotation or registration, then rebuild
    throw new IllegalStateException("Mapping bootstrap failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A collection attribute mapped as a single basic value (@JdbcTypeCode(SqlTypes.ARRAY), @Array, or plain unannotated collection) whose element type — a custom class, Object, or unresolvable generic — has no registered basic JavaType; entity-valued collections missing their relationship annotation so elements resolve as unknown basics.

Common situations: Forgetting @ElementCollection on a List<String>/List<enum> mapped to an array column in Hibernate 6.x; trying to persist List<OwnClass> without registering a type for OwnClass; raw collection fields without generics; annotation imports missing so @OneToMany silently does not apply.

Related errors


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