Bug description
After the jSpecify nullability refinement in commit 62c4cbf, RecordFieldSetMapper.mapFieldSet() rejects null values for types where null is a valid value (e.g. Long, Integer, BigDecimal, LocalDate).
When reading a CSV where a field is empty or blank, the SimpleTypeConverter correctly converts it to null. However, the Assert.notNull that was added in the commit above rejects this value, causing an IllegalArgumentException.
In Spring Batch 5, null was accepted and the record was created successfully with the field set to null.
Environment
- Spring Batch: 6.0.3
- Spring Boot: 4.0.5
- Java: 21
Minimal Complete Verifiable Example
Based on the spring-batch-mcve template:
Record:
public record MyRecord(String name, Long value) {}
Test:
@Test
void recordFieldSetMapperShouldAcceptNullForNullableTypes() {
RecordFieldSetMapper<MyRecord> mapper = new RecordFieldSetMapper<>(MyRecord.class);
DefaultFieldSet fieldSet = new DefaultFieldSet(
new String[]{"test", ""},
new String[]{"name", "value"}
);
// This succeeds in Spring Batch 5, fails in Spring Batch 6
MyRecord result = mapper.mapFieldSet(fieldSet);
assertNotNull(result);
assertEquals("test", result.name());
assertNull(result.value()); // null is a valid value for Long
}
Expected: MyRecord[name=test, value=null]
Actual: IllegalArgumentException: Cannot convert field 'value' to required type 'java.lang.Long'
Root cause
In RecordFieldSetMapper.mapFieldSet(), the following assertion was added:
Object converted = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
Assert.notNull(converted, // ← rejects valid null for nullable types
() -> String.format("Cannot convert field '%s' to required type '%s'", name, type.getName()));
The Assert.notNull check rejects all null values after conversion. This is too strict for reference types such as Long, Integer, BigDecimal, or LocalDate, where null may be valid. In Spring Batch 5, this assertion was not present.
// v5
args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
Suggested fix
Remove the Assert.notNull check to restore the v5 behavior:
// Same as v5
args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
Attached projects
The following zip files contain runnable MCVE projects. Run with mvn test.
spring-batch-mcve-v6.zip: Test fails with IllegalArgumentException
spring-batch-mcve-v5.zip: Test passes, value = null
To reproduce, run:
$ unzip spring-batch-mcve-v6.zip && cd spring-batch-mcve-v6
$ mvn test
Related
Commit: 62c4cbf ("Refine nullability checks with jSpecify")
Discussion
Bug description
After the jSpecify nullability refinement in commit
62c4cbf,RecordFieldSetMapper.mapFieldSet()rejectsnullvalues for types where null is a valid value (e.g.Long,Integer,BigDecimal,LocalDate).When reading a CSV where a field is empty or blank, the
SimpleTypeConvertercorrectly converts it tonull. However, theAssert.notNullthat was added in the commit above rejects this value, causing anIllegalArgumentException.In Spring Batch 5,
nullwas accepted and the record was created successfully with the field set tonull.Environment
Minimal Complete Verifiable Example
Based on the spring-batch-mcve template:
Record:
Test:
Expected:
MyRecord[name=test, value=null]Actual:
IllegalArgumentException: Cannot convert field 'value' to required type 'java.lang.Long'Root cause
In
RecordFieldSetMapper.mapFieldSet(), the following assertion was added:The
Assert.notNullcheck rejects allnullvalues after conversion. This is too strict for reference types such asLong,Integer,BigDecimal, orLocalDate, wherenullmay be valid. In Spring Batch 5, this assertion was not present.Suggested fix
Remove the Assert.notNull check to restore the v5 behavior:
Attached projects
The following zip files contain runnable MCVE projects. Run with mvn test.
spring-batch-mcve-v6.zip: Test fails with IllegalArgumentException
spring-batch-mcve-v5.zip: Test passes, value = null
To reproduce, run:
Related
Commit: 62c4cbf ("Refine nullability checks with jSpecify")
Discussion