spring-batch icon indicating copy to clipboard operation
spring-batch copied to clipboard

Kotlin data class support for `FlatFileItemReaderBuilder`

Open 0x1306e6d opened this issue 1 year ago • 4 comments

Please do a quick search on Github issues first, the feature you are about to request might have already been requested.

Expected Behavior

Make FlatFileItemReaderBuilder detect whether the target type is Kotlin data class and sets proper FieldSetMapper which is not BeanWrapperFieldSetMapper.

RecordFieldSetMapper works with a Kotlin data class but we might be better to introduce a new dedicated FieldSetMapper.

Current Behavior

The FlatFileItemReaderBuilder only detects whether the target type is record or not. So it sets the BeanWrapperFieldSetMapper which instantiates the target type by the default constructor (no-args) and causes NotWritablePropertyException:

https://github.com/spring-projects/spring-batch/blob/d1bd7711c3947020cd786bd7f45dc26720bef430/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemReaderBuilder.java#L463-L467

Context

As a workaround, we might make the data class fields nullable and mutable like:

data class Player(var lastName: String? = null)

but as you know, it doesn't leverage the Kotlin's features.

0x1306e6d avatar Mar 27 '24 08:03 0x1306e6d

Thank you for the suggestion.

Make FlatFileItemReaderBuilder detect whether the target type is Kotlin data class

Is there a reliable way to detect that? My concern with this request is that it would introduce code that is specific to Kotlin data classes (and possibly other JVM languages in the future if we get similar requests). As you mentioned, RecordFieldSetMapper works with a Kotlin data class so Kotlin users can just set that mapper or a custom one directly. Wdyt?

fmbenhassine avatar May 07 '24 13:05 fmbenhassine

As you mentioned, RecordFieldSetMapper works with a Kotlin data class so Kotlin users can just set that mapper or a custom one directly.

You makes sense.

But when both targetType and fieldSetMapper are specified in the FlatFileItemReaderBuilder, the builder doesn't use the fieldSetMapper and do create a new one by the targetType. It might be better to warn when the fieldSetMapper is suppressed by the targetType.Supporting data class when the targetType is specified is just another improvement for Kotlin users.

Is there a reliable way to detect that? My concern with this request is that it would introduce code that is specific to Kotlin data classes (and possibly other JVM languages in the future if we get similar requests).

My first idea was that using KotlinDetector and JvmClassMappingKt as Spring Framework does:

https://github.com/spring-projects/spring-framework/blob/168276aaab14456cfcc1b0bd440a26e0682f8f90/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java#L120-L121

https://github.com/spring-projects/spring-framework/blob/168276aaab14456cfcc1b0bd440a26e0682f8f90/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java#L223-L224

Optional Kotlin dependency would not affect users who don't use Kotlin.

Or we'd be better to introduce a FieldSetMapperFactory that creates a new FieldSetMapper by the given targetType so that other JVM languages can easily integrate with. e.g.:

if (this.targetType != null || StringUtils.hasText(this.prototypeBeanName)) {
  // this.fieldSetMapperFactories contains BeanWrapperFieldSetMapper and RecordFieldSetMapper by default
  for (FieldSetMapperFactory factory : this.fieldSetMapperFactories) {
    if (factory.canMap(this.targetType) {
      lineMapper.setFieldSetMapper(factory.create(this.targetType);
      break;
    }
  }
}

0x1306e6d avatar May 08 '24 04:05 0x1306e6d

Is there a reliable way to detect that?

I've filled an issue to spring-framework about providing a way to detect a Kotlin data class.

https://github.com/spring-projects/spring-framework/issues/32937

0x1306e6d avatar Jun 02 '24 08:06 0x1306e6d

I have decline the Framework issue created, and would advise to use KotlinDetector#isKotlinType to detect Kotlin classes and then using nested classed providing static methods using kotlin-reflect API from Java to perform such test, like what is done here.

sdeleuze avatar Jun 03 '24 14:06 sdeleuze

Thank you for the updates, @0x1306e6d ! Based on @sdeleuze feedback, I would keep this type detection and mapper decision on the user's side rather than the framework side.

Thank you both for the follow-up on this. Appreciated your time!

fmbenhassine avatar Oct 14 '24 16:10 fmbenhassine