hibernate/hibernate-orm · error · AnnotationException

Column mappings for property '${propertyName}' mix nullable

Error message

Column mappings for property '${propertyName}' mix nullable with 'not null'

What it means

AnnotatedColumns.checkPropertyConsistency compares each pair of consecutive non-formula columns that map one property; if one column is nullable and the next is not, the mapping is self-contradictory and binding throws this AnnotationException. The same pass also rejects mixed insertable/updatable flags and distinct secondary tables.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java:164

		return holder.getTable();
	}

	public void setTable(Table table) {
		this.table = table;
	}

	public void addColumn(AnnotatedColumn child) {
		columns.add( child );
	}

	public void checkPropertyConsistency() {
		if ( columns.size() > 1 ) {
			for ( int currentIndex = 1; currentIndex < columns.size(); currentIndex++ ) {
				final AnnotatedColumn current = columns.get( currentIndex );
				final AnnotatedColumn previous = columns.get( currentIndex - 1 );
				if ( !current.isFormula() && !previous.isFormula() ) {
					if ( current.isNullable() != previous.isNullable() ) {
						throw new AnnotationException(
								"Column mappings for property '" + propertyName + "' mix nullable with 'not null'"
						);
					}
					if ( current.isInsertable() != previous.isInsertable() ) {
						throw new AnnotationException(
								"Column mappings for property '" + propertyName + "' mix insertable with 'insertable=false'"
						);
					}
					if ( current.isUpdatable() != previous.isUpdatable() ) {
						throw new AnnotationException(
								"Column mappings for property '" + propertyName + "' mix updatable with 'updatable=false'"
						);
					}
					if ( !current.getExplicitTableName().equals( previous.getExplicitTableName() ) ) {
						throw new AnnotationException(
								"Column mappings for property '" + propertyName + "' mix distinct secondary tables"
						);
					}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Make nullable identical across all columns of the property (usually all false when part of a key)
  2. If parts genuinely differ, split the mapping into separate properties so each has a consistent nullability
  3. Re-run the SessionFactory build to surface the next consistency check (insertable/updatable, table) if any

Example fix

// before: contradictory nullability within one property
@Type(MoneyType.class)
@Columns({
    @Column(name = "amount", nullable = false),
    @Column(name = "currency", nullable = true)    // mixes with false -> error
})
private Money price;

// after: consistent nullability
@Type(MoneyType.class)
@Columns({
    @Column(name = "amount", nullable = false),
    @Column(name = "currency", nullable = false)
})
private Money price;
Defensive patterns

Strategy: validation

Validate before calling

// Before boot: all columns of one multi-column property must agree on nullable
static boolean columnGroupsConsistent(Class<?> entity) {
    for (Field f : entity.getDeclaredFields()) {
        Columns cols = f.getAnnotation(Columns.class);
        if (cols == null || cols.value().length < 2) continue;
        boolean first = cols.value()[0].nullable();
        for (Column c : cols.value()) {
            if (c.nullable() != first) return false;
        }
    }
    return true;
}

Try / catch

try {
    final SessionFactory sf = new MetadataSources(standardServiceRegistry)
            .addAnnotatedClass(MyEntity.class)
            .buildMetadata()
            .buildSessionFactory();
} catch (AnnotationException | MappingException e) {
    throw new IllegalStateException("Invalid ORM mapping, aborting startup: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A single property mapped by multiple @Column/@AttributeOverride entries whose nullable settings differ - e.g. @Columns({@Column(nullable = false), @Column(nullable = true)}) or an @AttributeOverrides block where one overridden column is required and another is optional.

Common situations: Copying override blocks between properties and tweaking only one entry; composite types where one part was made NOT NULL for a legacy schema while the other stayed optional; partial edits after schema hardening.

Related errors


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