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
- Make nullable identical across all columns of the property (usually all false when part of a key)
- If parts genuinely differ, split the mapping into separate properties so each has a consistent nullability
- 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
- Edit all entries of a @Columns/@AttributeOverrides block together
- Prefer splitting genuinely-different-nullability data into separate properties
- Add a consistency check to mapping tests
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
- Column mappings for property '${propertyName}' mix insertabl
- Property '${path}' specifies ${columnCount} '@AttributeOverr
- Attribute was not a Map : ${collectionMemberType}
- Unable to create AttributeConverter instance
- '@ColumnDefault' may only be applied to single-column mappin
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/31ffab836d35b5e3.
Report an issue: GitHub.