hibernate/hibernate-orm · error · AnnotationException

Column mappings for property

Error message

Column mappings for property '${propertyName}' mix updatable with 'updatable=false'

What it means

Hibernate throws this at bootstrap while binding a property that maps to multiple columns (e.g. via @Columns, @JoinColumn + @Column, or overridden mappings). Every column mapping belonging to one property must agree on nullability, insertability, updatability, and secondary table; here two of them disagree on the 'updatable' flag, so Hibernate cannot decide what the UPDATE statements should include.

Solutions

  1. Find the property named in the message and set the SAME 'updatable' value on every @Column/@JoinColumn that maps it (default is true).
  2. Check @AttributeOverride / @AssociationOverride declarations in subclasses and embeddings for flags that differ from the parent mapping; make them consistent.
  3. If one column really must be read-only while others are writable, split them into two properties (one with updatable=false) instead of one property with mixed flags.

Example fix

// before
@Entity
class Person {
    @Columns({
        @Column(name = "first_name"),
        @Column(name = "last_name", updatable = false) // mixed flags
    })
    private Name name;
}

// after
@Entity
class Person {
    @Columns({
        @Column(name = "first_name"),
        @Column(name = "last_name") // consistent: both updatable
    })
    private Name name;
}
Defensive patterns

Strategy: validation

Validate before calling

// Startup self-check: fail with a clear report before Hibernate binds
for (Class<?> cls : persistenceUnitClasses) {
    for (Field f : cls.getDeclaredFields()) {
        Column[] cols = f.getAnnotationsByType(Column.class);
        Set<Boolean> upd = Arrays.stream(cols).map(Column::updatable).collect(toSet());
        if (cols.length > 1 && upd.size() > 1) {
            throw new IllegalStateException(cls.getSimpleName() + "." + f.getName()
                + " mixes updatable flags across " + cols.length + " columns");
        }
    }
}

Try / catch

try {
    Metadata metadata = metadataBuilder.build(); // binding happens here
} catch (AnnotationException e) {
    // message names the property: "Column mappings for property 'x' mix updatable..."
    throw newConfigurationException("Inconsistent column flags", e);
}

Prevention

When it happens

Trigger: A property carries two column mappings where one is 'updatable=true' (the default) and the other is '@Column(..., updatable=false)'. Typical shapes: an @AttributeOverride/@AssociationOverride in a subclass that flips updatable on one column of a multi-column property; a @Column plus a @JoinColumn (or @JoinColumns) on the same association with mismatched flags; the same property mapped twice through an embedded and an override.

Common situations: Copying a read-only pattern ('insertable=false, updatable=false') onto only the first of several @Column entries of a property; overriding an embedded or mapped-superclass mapping in a subclass without repeating all flags; mixing JPA annotations with legacy XML hbm mappings where one side sets updatable=false.

Related errors


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

Appendix: source

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

	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)